Harrish A
Harrish A

Reputation: 73

Mock Scala classes that take parameters

I am writing unit tests for a class(class A) that takes another class(class B) as its parameter which in turn takes another class(class C) as its parameter. I need to mock class B in order to test class A. But, mock doesn't take class parameters.

So, I created a trait(trait BsTrait) and my class B extends BsTrait. According to this answer - (ScalaMock. Mock a class that takes arguments).

Class I want to test - class A(b: BsTrait){} class B - class B(c: C){} class C - class C{} trait of class B - trait BsTrait{}

My Unit Test -

    val mockFactory = mockFunction[C, B]
    val mockClient = mock[BsTrait]
    mockFactory.expects(new C).returning(mockClient)``` 


    Error: /path/to/file/Test.scala:63: type mismatch; found : com.example.BsTrait   required: com.example.B
mockFactory.expects(new C).returning(mockClient)

I tried to add return type for mockFunction as well, but it threw the same error.

Upvotes: 0

Views: 534

Answers (1)

Rohde Fischer
Rohde Fischer

Reputation: 1482

I know this is a not directly a solution, and maybe it actually is.

In my experience, ditch the mocking framework in 99% of the cases where you want it, and just write mock implementations of traits (or interfaces in other languages). Yes it will feel like boilerplate, but:

  • Often it turns out you will write less code - the mock code is short to begin with, but almost always starts having all sorts of work arounds to allow all sorts of calls, sometimes even calls your test does not care about, but has to be there. That you will end up having in a lot of the tests, just for the sake of the test

  • Once you in your framework mock a function/method with some testing behaviour people often don't discover when it isn't needed for the test any more, and forget to delete it. Auch dead code that nothing warns us is dead

  • Code you control is easier to extend or modify to your needs. Your own problem as a good example. Had your mock just been a class you wouldn't need to ask a question. Now your mock is hidden i all sorts of reflections, perhaps byte code manipulations (I don't know if ScalaMock does this, but a lot of frameworks do), and most likely other nasty things, that is impossible to debug.

  • Fameworked mocks are often hard and unwieldy to maintain. If you add a method to your trait you can end needing to update a lot of tests, even though it should not affect them. Just because those tests in some hidden way use them

  • You don't fall into the mocking framework pitfall with testing how you implemented your function/method rather than what it does. (E.g. is it important if you rest client specifically calls the method named get, or is it important that it performs a get-request, no matter if it calls exchange, get or something else?)

So even though it feels like overkill, heavily consider doing it anyway. You Will most likely write less code in the long run. The code you write is more clear and readable, if a function or method is no longer used you remove it from your trait and get a compiler warning on your mock. Your question would automatically have been solved, because you'd just extend your mock's behavior just enough to get it going. And your are more likely to actually write good tests.

Lastly, if you follow this suggestion, just one thing: keep your mocks independent. Do not let B take a parameter C in your mock. Make your B work with lists, maps, or whatever you need. The point is to test A, not B and C :)

Upvotes: 0

Related Questions