我有一个关于单元测试的基本问题。我在我的代码中发现了一个崩溃,我写了一个测试来重现这个错误。然后我修复了这个错误并验证它通过了测试。我的问题是,我是否采取了最好的方法。这是一个简化的示例:
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")
}
}
}
在上面的示例中,我有一个名为“all”的方法,它调用方法 a、b、c、d、e、f。现在,我在“f”中发现了崩溃,因为“internalVar”不是预期值。这个“internalVar”是在“e”中设置的,但是正如你所看到的,如果条件为假,“e”不会将 internalFalg 设置为任何东西。所以错误在 e 中。
我能够编写测试来隔离错误。然后我修复了这个bug,发现测试过去了。太好了,但为了做到这一点,我必须这样做:
void testAll()
{
mockDep1 = mock(dep1);
mockDep2 = mock(dep2);
mockDep3 = mock(dep3);
mockDep4 = mock(dep4);
all(mockDep1, mockDep2, mockDep3, mockDep4);
}
现在,在这个例子中,我制作 mockDep1..4 的部分非常简单,但实际上是很长很费力的代码。我的问题是,仅仅做最少的调用来重现崩溃是否有效:
void testCrash()
{
mockDep3 = mock(dep3);
c(mockDep3)
e()
f();
}
这将是我重现和测试崩溃所需要做的所有事情,除了它不完全是在调用每个方法的实际方法“all”中调用代码的方式。似乎为每个错误一遍又一遍地编写 testAll 包括所有模拟有点乏味。但话又说回来,这就是实际方法“all”的工作方式。那么,即使在调用完整方法的情况下,隔离并不完全是它的工作方式,隔离 bug 是否更好?