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

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

有帮助吗?

解决方案

您的建议是有效的。除了你的代码覆盖问题,我认为它比使用 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))

其他提示

@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");
}

我知道这是一个老话题,但我遇到了同样的问题。

最后我质疑自己:为什么我需要知道测试的覆盖范围? 我没有! - 所以让我们排除它们,所以覆盖范围更清晰。

在我的测试项目中,我添加了 CodeCoverage.runsettings 文件,这是内容:

<?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>

选择此测试设置文件后,我的代码覆盖率为100%

这种方式没有必要'破解'单元测试代码覆盖系统,只是为了达到100%: - )

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top