Argument Capturing: A must know unit testing technique.
Tags: Java, Software Development, Unit Testing, Junit, Mockito

Argument Capturing: A must know unit testing technique. JAVING Follow Jun 22 · 3 min read

@Captor is an annotation in the mockito library that is used alongside the ArumentCaptor class to capture arguments that are passed to the methods of mocked objects. It is always used alongside verify() to retrieve the arguments that were passed when a method from a mocked dependency is called. It is a useful tool that can enable us to create extra assertions to our tests and therefore make our unit tests more accurate.

Here we have a GuestsBook class that will save a GuestEntry into a the guestsBookRepository. The guestsBookRepository is a dependency of the class and if we were to write a unit test, we would have to mock it. Do you think a mock will be enough to accurately test the this class?

If we were to mock the dependency of this class and try to test it. The only thing we would be able to do, would be to just verify that there was an interaction with the guestsBookRepository object, but we would not be able to check the data that was passed accurately.

The reason for this is that we don’t have control over the GuestBookUtils class which is used to generate guestEntry. This constraint in unit testing terminology is called hard wired dependency. We say that is hard wired because the class at the moment has no mechanism to inject it.

This test is inaccurate because we are not checking the values that we are passing to the save() method

If at this point we were to take some time to think about OOD(Object Oriented Design), maybe we would reach the conclusion that something needs to be done with GuestBookUtils to allow our selves to test more accurately the class. Should we get rid of the static method call? Should we inject a factory? There are probably multiple things that we could make this class a bit more testable. But is likely that those would require refactoring.

There’s nothing wrong with refactoring, I am pro-refactoring and I believe we should always strive to improve the quality of our code-base. Unfortunately in real life sometimes we have to postpone this kind of decisions, specially in very large and convoluted code bases or maybe when we need to meet a tight deadline.

Of course the side effect of postponing refactoring will be the generation of technical debt. Maybe we will talk more about refactoring in another story but now let’s have a look at how @Captor can help us here now, without altering our production code.

Maybe is the right thing to do, but is not the right moment.

In this alternative implementation of our unit test, we will use @Captor to create a more accurate version of the test. Notice that we have extra assertions now. This is possible because our guestEntryCaptor is recording the values that are passed to the save() method.

A more accurate test is possible thanks to the @Captor annotation

The mechanics are very simple, here’s a summary of the steps:

Create an object of type ArgumentCaptor using the generic parameter to specify the type of object that you want to capture. Annotate that object with the @Captor annotation. In your usual mock verification call the capture() method to enable the capturing Use the getValue() method of your captor object to get the recorded values and assign them to a variable. Add the additional assertions