我试图更密切地遵循“每个单元测试一个断言”的做法。这并不总是可能的,但我确实发现它对于查明错误很有用。
例如,我可能有类似的东西:
var toTest;
[TestInitialize]
Init() {
toTest = // Create toTest
}
[TestMethod]
TestIsCreated() {
Assert.IsNotNull(toTest);
}
[TestMethod]
TestIsProperty1Setup() {
Assert.IsNotNull(toTest.Property1);
// Maybe some more tests on Property1
}
// More tests for other properties...
问题是如果 toTest 的创建失败,那么所有其他单元测试也会失败。所以我们可以像这样添加一些检查:
...
[TestMethod]
TestIsProperty1Setup() {
if (toTest == null) Assert.Inconclusive()
Assert.IsNotNull(toTest.Property1);
// Maybe some more tests on Property1
}
...
这将停止级联故障并指出确切的问题。如果 // Create toTest 行返回 null,那么我会得到一个单元测试失败,以及一堆更不确定的测试。当我修复一个失败的测试时,其他一切都通过了。
有两个地方会出现这种情况。一是我现在在几乎所有单元测试的开始都有重复代码。
第二个问题是当我想设置和检查一个更复杂的对象时(在这个虚拟示例中 toTest 也有一个数组):
[TestMethod]
TestArrayIsNotNull() {
if (toTest == null) Assert.Inconclusive();
Assert.IsNotNull(toTest.Array);
}
[TestMethod]
TestArrayIsInitilizedWithOneElement() {
if (toTest == null) Assert.Inconclusive();
if (toTest.Array == null) Assert.Inconclusive();
Assert.AreEqual(1, toTest.Array.Count())
}
[TestMethod]
TestArrayIsInitilizedWithCorrectElement() {
if (toTest == null) Assert.Inconclusive();
if (toTest.Array == null) Assert.Inconclusive();
if (toTest.Array.Count() != 1) Assert.Inconclusive();
// Assert something about toTest.Array[0]
}
现在每个单独的测试都有更多重复的代码,更重要的是,断言必须在不同的测试之间保持同步,这意味着一个小的变化可以在许多单元测试中产生涟漪。
理想情况下,我希望有一个位于每个测试顶部的属性,并且只有在“先决条件”测试通过时才运行测试。它可能看起来像这样:
[TestMethod]
[OnlyRunIfOtherTestPasses(TestIsCreated())]
TestArrayIsNotNull() {
Assert.IsNotNull(toTest.Array);
}
[TestMethod]
[OnlyRunIfOtherTestPasses(TestArrayIsNotNull())]
TestArrayIsInitilizedWithOneElement() {
Assert.AreEqual(1, toTest.Array.Count())
}
[TestMethod]
[OnlyRunIfOtherTestPasses(TestArrayIsInitilizedWithOneElement())]
TestArrayIsInitilizedWithCorrectElement() {
// Assert something about toTest.Array[0]
}
单元测试现在非常简单 - 没有重复并且保持测试与其“先决条件”同步现在是自动的。
有这样的事情吗?或者我可以使用完全不同的功能来获得相同的效果?
我知道将属性中的测试方法引用到另一个测试方法几乎肯定存在问题 - 您可能必须使用硬编码到方法名称的字符串(所以如果方法名称更改会发生什么情况,或者该方法不存在)。我确信循环引用也存在潜在问题。但是,尽管存在所有这些问题,但一定有人已经解决了这个问题。谁能给我指出来?