So in the previous post we talked about some resources and concepts we should understand before writing JUnit tests with Mockito.
Well, based on my recent experience, they may not be enough, especially when our code has many dependencies or collaborators and we have to mock all of them to do the test. Things can become complicated very soon. If you find yourself in this situation, stop writing the test and refactor your code first, for example, by using the strategy pattern. In this way, you will likely have less collaborators to mock, but of course, you need to write tests for code delegated to other classes. There may be some extra work, but cleaner and easier.
Aside from this, my recent struggle also made me think about how to write unit test in general. I found another post about unit testing by Martin Fowler and read the first part of a book called “Effective Unit Testing”. There are some paragraphs that I find very helpful:
1) “We write tests assuming everything other than that unit under test is working correctly.” — Martin Fowler
Here “other than that unit under testing” usually means collaborators and we want to isolate them from the code under testing. By isolating them, we mean using test doubles to replace the real collaborators.
2) “A test should test just one thing and test it well while communicating its intent clearly.” We must ask ourselves:
What is the desired behavior for the code under testing? Think about how each step of your method should behave with certain inputs. The desired behavior of the dependencies is something we should configure the mocks to expect.
A general process could be:
Q: What do we want to test?
A: We want to test if method A is working properly.
Q: What do you mean by working properly?
A: By properly I mean that with a certain type of input INPUT, A should first call a dependency with INPUT and return a RESPONSE. Then A does some calculation by adding a number to some value stored in RESPONSE and return the sum. I expect that the sum is XXX.
Q: OK, so here we have a dependency. How do we deal with it?
A: We can isolate it by using a mock object to stub the RESPONSE when called with INPUT.
Q: Are there any requirements on the RESPONSE object?
A: Yes, since we are using some value in the RESPONSE, we should stub that value in the RESPONSE object. Otherwise, we might hit a null pointer exception situation.
Anyway, the key point is that we shouldn’t have too many dependencies for mocking in one unit test. Refactor the code first. Then work slowly through this process. Take your time.