CodeCoverageとExpectedException
-
05-07-2019 - |
質問
このパターンのユニットテストがいくつかあります:
[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アクションパラメーターが予想される例外とは別の例外をスローする場合、ExpectExceptionは期待どおりに動作しません。
[TestMethod]
public void my_test()
{
ExpectException<InvalidOperationException>(delegate()
{
throw new ArgumentException("hello");
});
}
TestMethod&quot; my_test&quot;を実行するとテストメソッドが発生し、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%を達成するためだけに、ユニットテストコードカバレッジシステムを「ハッキング」する必要はありません:-)
はい、これはかなり標準的な料金です-私たちのテストの多くは同じことをします。同時に、それらのハーフブランチが努力の価値があるためにあまりにも重くなっている場合、コードカバレッジにあまり高い値を置いていないのではないかと思う必要があります。