我有几种这种模式的单元测试:
[TestMethod ()]
[ExpectedException (typeof (ArgumentNullException))]
public void DoStuffTest_Exception ()
{
var foo = new Foo ();
Foo.DoStuff (null);
}
事实证明,代码覆盖率将抛出行标记为半运行,因此每次都会获得1块未覆盖的代码。
在考虑了这个问题一段时间之后,我能想出的最佳解决方案是添加一个try / catch。由于这是一个重复的模式,我将按照
的方式创建一个辅助方法public static void ExpectException<_T> (Action action) where _T: Exception
{
try { action(); }
catch (_T) { return; }
Assert.Fail ("Expected " + _T);
}
这可以带来很好的附带好处,我可以将所有异常测试添加到非投掷测试中。
这是一个有效的设计,还是我错过了什么?
编辑: Ugs ...似乎上面的ExpectException方法也让我看到了1个未覆盖的块。
答案 0 :(得分:10)
您的建议是有效的。除了你的代码覆盖问题,我认为它比使用ExpectedException
属性更好,因为它明确显示了测试的哪一行应该抛出异常。使用ExpectedException
表示测试中的任何代码行都可以抛出预期的异常类型,测试仍然会通过。如果错误来自另一个不希望抛出的调用,它可以掩盖测试应该失败的事实,因为应该抛出的行不是。
对你提议的内容有什么有用的修改是返回被捕获的异常:
public static _T ExpectException<_T> (Action action) where _T: Exception
{
try { action(); }
catch (_T ex) { return ex; }
Assert.Fail ("Expected " + typeof(_T));
return null;
}
这将使测试代码能够在需要时进一步断言异常(即检查使用的特定消息)。
NUnit(虽然看起来你没有使用它,因为你有一个TestMethod
属性)有一个类似于你建议的内置结构:
Assert.Throws<ArgumentNullException>(() => Foo.DoStuff(null))
答案 1 :(得分:3)
@adrianbanks如果action参数抛出另一个异常而不是预期的异常,则ExpectException不会按预期工作:
[TestMethod]
public void my_test()
{
ExpectException<InvalidOperationException>(delegate()
{
throw new ArgumentException("hello");
});
}
当我执行TestMethod“my_test”时,我收到一条消息,说明测试方法已经引发并且System.ArgumentException:hello。在这种情况下,它应该显示“Expected InvalidOperationException”。 我为ExpectException方法提出了一个新版本:
public static void VerifierException<T>(Action action) where T : Exception
{
try
{
action();
}
catch (Exception ex)
{
Assert.IsInstanceOfType(ex, typeof(T));
return;
}
Assert.Fail("Aucune exception n'a été déclenchée alors qu'une exception du type " + typeof(T).FullName + " était attendue");
}
答案 2 :(得分:2)
我知道这是一个老话题,但我遇到了同样的问题。
最后我质疑自己:为什么我需要知道测试的覆盖范围? 我没有! - 所以让我们排除它们,所以覆盖范围更清晰。
在我的测试项目中,我添加了一个<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Configuration>
<CodeCoverage>
<ModulePaths>
<Exclude>
<ModulePath>.*tests.dll</ModulePath>
<ModulePath>.*Tests.dll</ModulePath>
<!-- Add more ModulePath nodes here. -->
</Exclude>
</ModulePaths>
</CodeCoverage>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
文件,这是内容:
ThreadPoolExcutor
选择此测试设置文件后,我的代码覆盖率为100%
这种方式没有必要'破解'单元测试代码覆盖系统,只是为了达到100%: - )
答案 3 :(得分:0)
是的,这是非常标准的票价 - 我们的很多测试都是这样做的。与此同时,你不得不怀疑,如果这些半分支的重量太大而不值得付出代价,那么你是否不会对代码覆盖率设置过高的价值。