views:

128

answers:

4

After reading an interesting article about unit testing behavior instead of state, I came to realize that my unit tests often are tightly coupled to my code because I am using mocks. I cannot image writing unit tests without mocks but the fact is that these mocks are coupling my unit test very much to my code because of the expect andReturn calls.

For example when I create a test that uses a mock, I record all calls to the specific mock and assign return values. Now when I change the implementation of the actual code for whatever reason, a lot of tests break because that call was not expected by the mock, forcing me to update the unit test also, and effectively forcing me to implement every change twice... This happens a lot.

Is this issue intrinsic to using mocks, and should I learn to live with it, or am I doing something fundamentally wrong? Please enlighten me :) Clear examples coming with the explanation are most welcome of course.

+1  A: 

If you are fixing the tests because they break, you are not using them as intended.

If the behaviour of a method changes, in test driven development you would first change the test to expect the new behaviour, then implement the new behaviour.

Guffa
That is correct, and a very concise definition of TDD, but it doesn't change the fact that you always have to implement changes twice, first in the test, see it fail, then in the code, see it succeed. So is this 'always implement twice issue' intrinsic to TDD?
nkr1pt
@nkr1pt: Yes, but it's not isolated to TDD. You would have more or less the same issue in any development model that incorporates testing in any form. It's just that it's so obvious in TDD.
Guffa
+4  A: 

My experience is to use mocks only at the bounderies of (sub)systems. If I have two classes that are strongly related I do not mock them appart but test them together. An example might be an composite and a visitor. If I test a concrete visitor I do not use a mock for the composite but create real composites. One might argue that this is not a unit test (depends on the definition of what is a unit). But that doesn't matter that much. What I try to achieve is:

  1. Write readable tests (tests without mocks are most of the time more easy to read).
  2. Test only a focused area of code (in the example the concreate visitor and the relevant part of the composite).
  3. Write fast tests (as long as I instantiate only a few classes, in the example the concrete composites, this is not a concern ... watch for transitive creations).

Only if I encounter the boundary of a subsystem I use mocks. Example: I have a composite that can render itself to a renderer I would mock out the renderer if I test the render logic of the composite.

Testing behavior instead of state looks promosing at first but in general I would test state as the resulting tests are easiear to maintain. Mocks are a cannon. Don't crack a nut with a sledgehammer.

Arne
While I agree with the sense of this, a lot of systems are shallow enough in enough places that there's very little difference between mocking all the time and mocking only at subsystem boundaries.
Mike Burton
In this case: give the systems some depth. Clearly seperate the subsystems!
Arne
If you feel good testing a number of classes together as a unit, maybe the next step for you is to consolidate that unit into a single class.
Ladlestein
Maybe ... but perhaps to pack all the functionality into a single class makes the class do to much.
Arne
+4  A: 

when I create a test that uses a mock, I record all calls to the specific mock and assign return values

It sounds like you may be over-specifying expectations.

Try to build as little setup code as possible into your tests: stub (rather than expect) all behavior that doesn't pertain to the current test and only specify return values that are absolutely needed to make your test work.

This answer includes a concise example (as well as an alternative, more detailed explanation).

Jeff Sternal
A: 

Several good answers here already, but for me a good rule of thumb is to test the requirements of the method, not the implementation. Sometimes that may mean using a mock object because the interaction is the requirement, but you're usually better off testing the return value of the method or the change in state of the object.

glaxaco