C# CodeOverage与ExpectedException

C# CodeOverage与ExpectedException,c#,unit-testing,expected-exception,C#,Unit Testing,Expected Exception,我对这种模式进行了几个单元测试: [TestMethod ()] [ExpectedException (typeof (ArgumentNullException))] public void DoStuffTest_Exception () { var foo = new Foo (); Foo.DoStuff (null); } 事实证明,代码覆盖率将抛出行标记为半运行,因此每次我得到1块未覆盖的代码 在考虑这个问题一段时间后,我能想到的最佳解决方案是添加一个try/ca

我对这种模式进行了几个单元测试:

[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);
}
publicstaticvoidexpectexception(Action-Action),其中_T:Exception
{
试试{action();}
catch(_T){return;}
Assert.Fail(“预期”+\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;
}
[TestMethod]
public void my_test()
{
    ExpectException<InvalidOperationException>(delegate()
    {
        throw new ArgumentException("hello");
    });
}

@adrianbanks如果操作参数引发的异常与预期异常不同,则ExpectException不会按预期工作:

public static _T ExpectException<_T> (Action action) where _T: Exception
{
    try { action(); }
    catch (_T ex) { return ex; }
    Assert.Fail ("Expected " + typeof(_T));
    return null;
}
[TestMethod]
public void my_test()
{
    ExpectException<InvalidOperationException>(delegate()
    {
        throw new ArgumentException("hello");
    });
}
[TestMethod]
公共无效my_测试()
{
ExpectException(委托()
{
抛出新的ArgumentException(“hello”);
});
}
当我执行TestMethod“my_test”时,我刚刚收到一条消息,说测试方法引发了System.ArgumentException:hello。在这种情况下,它应该说“预期的无效操作异常”。 我为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");
}
publicstaticvoidverifierexception(Action-Action),其中T:Exception
{
尝试
{
动作();
}
捕获(例外情况除外)
{
IsInstanceOfType(例如,typeof(T));
返回;
}
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>

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


这样就不需要“黑客”单元测试代码覆盖率系统,只为了达到100%:-)

目前覆盖率高达可怕的35%,所以这不会增加太多。这更多的是一个小的设计问题(可能会节省几百行测试代码)。+1。好东西。这个问题困扰了我一段时间,但我一直没有时间去解决它。谢谢你的这个想法!我目前无法亲自尝试,但这似乎是一个非常好的解决方案,如果你只是确保测试本身是正确的。