Is it a code smell to spy on an object that is being unit tested? For example say I have a LineCounter
class whose job is to simply count the number of lines in a string. --
class LineCounter {
public int getNumLines(String string) {
String metadata = getStringMetadata(string);
// count lines in file
return numLines;
}
/** Expensive operation */
protected String getStringMetadata(String string) {
// do stuff with string
}
}
Now I want to write a JUnit 4 test for this to test the getNumLines
method while mocking out the expensive getStringMetadata
call. I decide to use Mockito's spy mechanism to have getStringMetadata
return a dummy value.
class LineCounterTests {
@Test public void testGetNumLines() {
LineCounter lineCounterSpy = Mockito.spy(new LineCounter());
// Mock out expensive call to return dummy value.
Mockito.when(lineCounterSpy.getStringMetadata(Mockito.anyString()).thenReturn("foo");
assertEquals(2, lineCounterSpy.getNumLines("hello\nworld");
}
}
Is this a reasonable thing to do? I feel pretty weird testing a Spy object rather than the actual class, but I can't really think of a reason against it.
I will answer the question in two parts. First, yes it is code smell to mock or spy the class under test. That does not mean that it cannot be done correctly but that it is risk prone and should be avoided whenever possible.
WRT your specific example, I would see how the spy could be correctly used but that would be predicated on the assertion that you have elsewhere fully unit tested getStringMetadata
. This then begs the question, if you have fully unit tested getStringMetadata
elsewhere then you must know how to test it and therefore why not test getNumLines
without the spy.
All this being said, millhouse
makes a good point but either way you have to unit test the expensive code somewhere. His suggestion goes a long way to help isolate the expensive code and ensure that you only have to test / exercise it once.
In this situation, it is perfectly legitimate to stub the method that is called by the method under test. It is even the only way I can think of to test it in isolation. You just don't want to extract a single method into it's own class for the sole purpose of testing.
Beware of the side effects in the stubbed method though. It might not be sufficient to stub the returned value, if the stubbed method has side effects then you have to stub the side effects as well. It might even be a reason against it in some situations where the side effects are very complex, but that would most likely be an indication of a code smell in the implementation of the class under test itself.
To answer your question, I find it easy to find reasons for it, but hard to find reasons against it. It's the technique I use every day, it helps me split my implementation in small methods that are tested individually in complete isolation, and I haven't seen any limitation to it yet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With