Mocking final vals

I tend to use much less features from testing/mocking libraries now that I use scala nowadays, but basic mocking is still something I rely upon in my tests. I have an issue however with _final val_s, in that they seem pretty tricky to deal with them for mocking libraries. It seems PowerMock may be able to help with final, but not with val (not sure to what extent).

I was wondering how people approached this issue? Is there a workaround, or is there yet a better way to approach the issue of mocking in scala altogether? I mostly resort to that when it comes to testing behavior of classes that depends too heavily on the class’ members to reasonably be externalized. Ideally I’m looking for an approach that would be least (if at all) disruptive to the code being tested. So turning _val_s into _def_s would be pretty disruptive for instance!

Thanks!

Is the code you’re trying to mock in this case code that you wrote or library code?

I tend to use Mockito + ScalaTest and have had pretty good results. In the event that I have issues with final members from libraries, I can usually write my own shim interface into the library that doesn’t utilize final and mock based on that interface. It seems to handle val just fine. The one part where it can break down is if you start relying on implicit.

There’s also the side of this that’s “don’t use mocking unless you need to.” Unless I’m literally writing a unit test that tests how an external resource might behave I can usually avoid using actual mocking libraries.

Vals are still backed by fields, and can be manipulated with the usual java reflection shenanigans. However, the short answer is that you can’t do this for final vals: even if you get it to work once you’ll get strange behaviour sooner or later, because final val tells the compiler that it’s free to inline the value. Hence it might still use the old value in your other code, even after the change.

I’d also recommend not doing it with vals on singletons, since code can easily start assuming that they’re truly immutable.

It’s sadly pretty invasive, but I’d strongly suggest looking into some sort of dependency injection whenever you feel the urge to do this. Whether you do that with the cake pattern or some sort of framework matters less.

3 Likes

I always wondered why there is no mock library that modifies your code on the fly instead of trying to break through permissions barriers. While you could pierce private access modifier with reflection, final is impenetrable after compiler escape analysis catches the method.

So, there only universally applicable method I could imagine is to patch sources to produce test version, making special amendments to mocked methods. Recompile changed classes and test them after that.

Exactly what I was thinking, a library that would remove all final and turn all vals into vars and use that version for testing only. I guess one could do it at the source code level, but maybe it’s doable to do it on the AST directly? the trick with the source code approach would be to detect _private val_s such as a primary constructor’s (not explicitly stated as such)

With regard to the other answers, I guess I don’t understand how it’s not a more common problem? I can’t be the only one using final val + using mock. Do everyone actually write their code accounting for that specific issue (with approaches like the ones suggested), or is it just that people don’t write tests? :slight_smile:

That wouldn’t work with how SBT and most IDEs work today, because application code is compiled separately from tests. Basically, your application is compiled like a library that the tests then interact with. For your approach to work, you’d need to compile application+test in one go, completely separate from the compilation of the application itself.

Also, your approach would be very fragile, since it would break if anything caches it before you mock it (which being a final val generally says is safe), and you would also be unable to change it for different tests.

That wouldn’t work with how SBT and most IDEs work today

Of course you need to make extra work to gain extra functionality. I assume the task is not so complicated comparing to fuzzing testing and spawning test docker containers which is already implemented for some test suites.

Mock libraries continues to employ reflection technique simply because it works somehow for some cases and is available immediately without extra manipulations.

Also, your approach would be very fragile, since it would break if anything caches it before you mock it

Then you need to recompile entire codebase that depends on mocked variable.

and you would also be unable to change it for different tests

each test could produce separate jars.