Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TDD how to handle a change in a mocked object

In writing unit tests, for each object that the unit interacts with, I am taking these steps (stolen from my understanding of JBrains' Integration Tests are a Scam):

  1. Write a test in the unit to ensure it is sending the correct call and params to the collaborating object
  2. Write a test in the unit that ensures it handles all possible responses from the collaborating object. These responses are all mocked so the unit is tested in isolation.
  3. Write a test in the collaborating object to make sure it accepts the call and params.
  4. Write tests to make sure each possible response is sent back.

My question comes around when I decide to refactor an object that has responses mocked in step 2. If I change the way the object responds to a call, none of the tests that other objects have for that call will fail because they have all been mocked to match the old style. How do you keep mocks up to date with the objects they are mocking? Is there a best practice for this? Or have I completely misunderstood things and am doing it all wrong?

like image 947
Lee Quarella Avatar asked Mar 05 '12 21:03

Lee Quarella


People also ask

What is a mock object TDD?

Learn more. Mocking is the creation of an object that mimics another and sets expectations concerning its behavior. In testing, mocking replicates the behavior of a service, object, or process. There are a number of benefits that result from replacing an object with a mock for testing purposes.

What is the relevance of mocking and stubbing in TDD?

Mocks and stubs are very handy for unit tests. They help you to test a functionality or implementation independently, while also allowing unit tests to remain efficient and cheap, as we discussed in our previous post.

What happens when we mock an object?

A mock object makes use of the same interface as the element of code it is intended to imitate and is often used in unit testing to scrutinize the performance of an actual object.


1 Answers

I do it this way.

Suppose I have to change the responses from interface method foo(). I gather all the collaboration tests that stub foo() in a list. I gather all the contract tests for method foo(), or if I don't have contract tests, I gather all the tests for all the current implementations of foo() in a list.

Now I create a version control branch, because it'll be messy for a while.

I @Ignore (JUnit speak) or otherwise disable the collaboration tests that stub foo() and start re-implementing and re-running them one by one. I get them all passing. I can do this without touching any production implementation of foo().

Now I re-implement the objects that implement foo() one by one with expected results that match the new return values from the stubs. Remember: stubs in collaboration tests correspond to expected results in contract tests.

At this point, all the collaboration tests now assume the new responses from foo() and the contract tests/implementation tests now expect the new responses from foo(), so It Should All Just Work.(TM)

Now integrate your branch and pour yourself some wine.

like image 87
J. B. Rainsberger Avatar answered Oct 15 '22 19:10

J. B. Rainsberger