Java – Is Spying on Tested Class Bad Practice?

javaunit testing

I am working on a project where class internal calls are usual but the results are many times simple values. Example (not real code):

public boolean findError(Set<Thing1> set1, Set<Thing2> set2) {
  if (!checkFirstCondition(set1, set2)) {
    return false;
  }
  if (!checkSecondCondition(set1, set2)) {
    return false;
  }
  return true;
}

Writing unit tests for this type of code is really hard as I just want to test the condition system and not the implementation of the actual conditions. (I do that in separate tests.) In fact it would be better if I passed functions that implement the conditions and in tests I simply provide some mock. The issue with this approach is the noisiness: we use generics a lot.

A working solution; however, is to make the tested object a spy and mock out the calls to the internal functions.

systemUnderTest = Mockito.spy(systemUnderTest);
doReturn(true).when(systemUnderTest).checkFirstCondition(....);

The concern here is that the SUT's implementation is effectively changed and it may be problematic to keep the tests in sync with the implementation. Is this true? Is there best practice to avoid this havoc of internal method calls?

Note that we are talking about parts of an algorithm, so breaking it out to several classes may not be a desired decision.

Best Answer

Unit tests should treat the classes they test as black boxes. The only thing which matters is that its public methods behave the way it is expected. How the class achieves this through internal state and private methods does not matter.

When you feel that it is impossible to create meaningful tests this way, it is a sign that your classes are too powerful and do too much. You should consider to move some of their functionality to separate classes which can be tested separately.

Related Topic