我有一个关于单元测试的基本问题。我在代码中发现了崩溃,我写了一个测试来重现这个bug。然后我修复了错误并确认它通过了测试。我的问题是,如果我采取最好的方法。这是一个简化的例子:
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"如果条件为false,则不将internalFalg设置为任何内容。所以bug就在e。
我能够编写测试来隔离bug。然后我修复了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包括所有的模拟一遍又一遍,每个bug都有点tedius。但话说回来,那就是实际的方法" all"会工作。那么最好是隔离bug,即使隔离不完全是如何调用完整方法的呢?
答案 0 :(得分:0)
从您的示例中不清楚哪些方法是公开的,哪些是私有的(或受保护的或包私有的)。如果唯一的公共方法是名为all
的公共方法,那么只要有可能,这是您的测试应该调用的方法,除非您正在测试实际上只包含在其中一种方法中的复杂逻辑。
当您选择要编写哪些测试时,您应该专注于此类合同的内容。您的应用程序的其他部分期望此类提供哪些功能?根据功能的基础确定测试方法;不是你碰巧写的方法。
此外,如果Dependency类的唯一目的是支持ClassC的功能,那么你可以考虑编写其SUT是这个ClassC和Dependency的测试(有些人称之为“集成测试”,有些人称之为“单元测试,其中单元由两个类组成“)。
在您的特定情况下,您的类似乎提供了跨越方法运行的功能。因此,请测试功能 - 并测试可能导致功能以不同方式工作的不同方案。不要测试个别方法。特别是,您将在各种情况下调用all
方法进行大量测试;其中一些导致错误发生(至少在你修复bug之前),其中一些没有。
最后,为了帮助您考虑测试功能和方案,我认为您应该根据正在执行的功能和方案命名您的测试方法,而不是通过被调用的方法。特别是,我认为testAll
(事实上,任何由test
组成的名称后跟方法名称)都是测试方法的不良名称。但也许这是另一天要考虑的事情。