I have a basic question about unit tests. I found a crash in my code, and I wrote a test to reproduce the bug. I then fixed the bug and verified that it passes the test. My question, is if I took the best approach. Here is a simplified example:
public class ClassC
{
private int internalFlag;
void all(Dependency dep1, Dependency dep2, Dependency dep3, Dependency dep4)
{
a(dep1);
b(dep2)
c(dep3)
d(dep4)
e();
f();
}
void e()
{
if (some logic based on dep3)
{
internalFlag = 2;
}
}
void f()
{
if (internalFlag == 2)
{
Log("All is well");
}
else
{
Log("Crash occurs")
}
}
}
In the above example, I have a method called “all” that calls methods a, b, c, d, e, f. Now, I found a crash in “f” because the “internalVar” was not an expected value. This ‘internalVar” was set in “e”, but as you can see “e” does not set the internalFalg to anything if the condition is false. So the bug is in e.
I was able to write the test to isolate the bug. Then I fixed the bug and found that the test past. Thats great, but in order to do it I had to do something like:
void testAll()
{
mockDep1 = mock(dep1);
mockDep2 = mock(dep2);
mockDep3 = mock(dep3);
mockDep4 = mock(dep4);
all(mockDep1, mockDep2, mockDep3, mockDep4);
}
Now the part where I make mockDep1..4 is very simple here in this example, but it was actually very long laborious code. My question is, is it valid instead just to do the minimal calls to reproduce the crash:
void testCrash()
{
mockDep3 = mock(dep3);
c(mockDep3)
e()
f();
}
This would be all I would be required to do to reproduce and test the crash, except that its not exactly how the code would be called in the actual method “all”, that calls each method. It just seems like writing testAll above including all the mocks over and over again for each bug is a bit tedius. But then again, thats how the actual method “all” would work. So is it better instead to isolate the bug, even if the isolation is not exactly how it would work if the full method was called?
What’s not clear from your example is which of the methods are public and which are private (or protected or package-private). If the only public method is the one called
all, then wherever possible, this is the one that your tests should call, unless you’re testing complex logic that really is contained in just one of the methods.When you choose which tests to write, you should really focus on what the contract of this class is. What functionality does the rest of your application expect this class to provide? Base your test methods on what that functionality is; not on which methods you happen to have written.
Moreover, if the only purpose of the Dependency class is to support the functionality of ClassC, then you might consider writing tests whose SUT is this ClassC and Dependency together (some would call these “integration tests”, some would call them “unit tests, where the unit consists of two classes”).
In your particular case, it seems that your class is providing functionality that runs right across the division into methods. So test the functionality – and test the different scenarios that may cause the functionality to work differently. Don’t test the individual methods. In particular, you’ll have a number of tests that call the
allmethod in various scenarios; some of which lead to the error occurring (at least before you fix the bug) and some of which do not.Lastly, to help you think in terms of testing functionality and scenarios, I believe that you should name your test methods according to the functionality and scenario that are being exercised, not by the method that is being called. In particular, I consider
testAll(in fact, any name that consists oftestfollowed by the name of a method) to be a poor name for a test method. But maybe that’s something to think about on another day.