3

I am under the impression that interaction testing (verifying mock invocations) in general should be avoided.

When unit testing method postconditions should be asserted instead.

However there are cases that interaction testing cannot be avoided. In these rare cases I think interaction testing is allowable.

Cases where interaction testing is ok (but still do it mindfully)

  • interaction facilitates state change (state change through a service)
  • interaction is side-effectful (database access, network call through a service, invocation to an observer)

However there seems to be a lot of argument for interaction testing. Particularly, to make sure that:

  • invocations do happen (even for non side effectful interactions)
  • no extra invocations happen

I think these cases fall into the implementation detail category and should not be tested anymore. While these cases have a point, tests for most implementation scenarios will have these shortcomings. And I think that there should be some level of trust that no developer in the team will write code that say for example: updates an entity and saves it to the database on a query method (clear violation of LSP, CQS and principle of least astonishment).

In addition, in these cases, in my opinion, the unit test tend to mirror the code under test too closely, and fail unnecessarily when the code under test changes (regardless of effect on behavior) rendering the tests too brittle therefore making them lose their value.

What are your opinions on this matter?

  • 2
  • 4
    In general, this should be based on, "Do I care about the invocation happening?" and if yes, "Why do we care about the invocation happening?" If you don't care (e.g., calling some other function is incidental and you actually care only about the return value), don't mock it. If you do care and replacing it with a mock still provides a useful test, then consider it. That doesn't exclude stubbing your inputs; that can still be helpful if the normal input is complex. (But you generally want to avoid complex inputs as well.) In all cases, you should ask, "What is the value of this test?" – jpmc26 Feb 28 '17 at 7:24
4

Cases where interaction testing is ok (but still do it mindfully)

This seems like a sound rationale to live by. Too many people ban interaction tests because of their fragility but forget that they can win you confidence about correctness at a lesser cost. Mocks are easier to set up and run faster than the integration test equivalents at the boundaries of a system, when communicating with a DB, the file system, the network, etc.

I would add another case - when you're designing an application from the outside in and still in the process of discovering which big categories of classes are needed and the dependencies between them.

Interaction tests based on interfaces and mocks allow you to flesh out protocols, i.e. discussion contracts between components, without having to implement their inner workings right away. These tests can even be treated as mere scaffolding and deleted when the design is dry.

  • invocations do happen (even for non side effectful interactions)
  • no extra invocations happen

I think these cases fall into the implementation detail category and should not be tested anymore.

Yes, testing that something didn't happen seems superfluous most of the time. That's why I don't clutter my mock verifications with extra check options like Times.Once or assertions about methods that don't come into play in the test scenario. This speaks to the "mindful" use of mocks you're mentioning.

About non side effectful actions, there's theory and practice. I consider changing an object's state as a side effect. It cannot always be tested by state verification, because the object doesn't (shouldn't) always expose its internal state. It cannot always be verified through mocks either since most mocking frameworks (at least in statically typed languages) don't allow you to mock concrete types. There's a kind of grey area where technical constraints trump design guidelines.

If the action is purely non side effectful (i.e. a call to a referentially transparent function in FP lingo), I wouldn't test the interaction but the output of the function. But it is not often the case in OO programs and checking a call to a mock is sometimes a solution when the dependency is mockable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.