CodeCoverage与ExpectedException

时间:2009-10-22 10:51:50

标签: c# unit-testing expected-exception

我有几种这种模式的单元测试:

[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个未覆盖的块。

4 个答案:

答案 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)

是的,这是非常标准的票价 - 我们的很多测试都是这样做的。与此同时,你不得不怀疑,如果这些半分支的重量太大而不值得付出代价,那么你是否不会对代码覆盖率设置过高的价值。