Assert を使用して例外がスローされたことを確認するにはどうすればよいですか?
-
06-09-2019 - |
質問
Assert (または他の Test クラス) を使用して、例外がスローされたことを確認するにはどうすればよいですか?
解決
「のVisual Studioチームテスト」について、あなたがテストの方法にExpectedException属性を適用する表示されます。
ここではドキュメントからのサンプル:ユニットテストVisual Studioチームテストするとチュートリアル
[TestMethod]
[ExpectedException(typeof(ArgumentException),
"A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}
他のヒント
通常、あなたのテストフレームワークは、このための答えを持っています。しかし、それは十分な柔軟性ではない場合、あなたは常にこの操作を行うことができます:
try {
somethingThatShouldThrowAnException();
Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }
@Jonasが指摘するように、これはベースの例外をキャッチするために動作しません。
try {
somethingThatShouldThrowAnException();
Assert.Fail(); // raises AssertionException
} catch (Exception) {
// Catches the assertion exception, and the test passes
}
あなたは絶対に例外をキャッチしなければならない場合は、、あなたは()Assert.Failを再スローする必要があります。しかし、実際に、これはあなたがこれを手で書くべきではない兆候です。オプションのためのテストフレームワークを確認するか、あなたがテストするために、より意味のある例外を投げることができるかどうかを確認します。
catch (AssertionException) { throw; }
キャッチする例外の種類を指定するなど、 - あなたが好きなように、この手法を適応することができるはずです。あなたが唯一の特定の種類を期待する場合は、とオフcatch
ブロックを終了します:
} catch (GoodException) {
} catch (Exception) {
// not the right kind of exception
Assert.Fail();
}
これを実行するための私の好ましい方法は、例外と呼ばれる方法を記述して、ちょうど他のどのAssertメソッドのようにそれを使用することです。残念ながら、.NETは、あなたはそれが実際にAssertクラスでのビルドに属しているかのように、このメソッドを使用することはできませんので、あなたは、静的な拡張メソッドを記述することはできません。ただ、別のと呼ばれるMyAssertまたは類似した何かを作ります。クラスは次のようになります:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests
{
public static class MyAssert
{
public static void Throws<T>( Action func ) where T : Exception
{
var exceptionThrown = false;
try
{
func.Invoke();
}
catch ( T )
{
exceptionThrown = true;
}
if ( !exceptionThrown )
{
throw new AssertFailedException(
String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
);
}
}
}
}
それはあなたのユニットテストはこのようになっていることを意味します:
[TestMethod()]
public void ExceptionTest()
{
String testStr = null;
MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}
あなたのユニットテストの構文の残りの部分のように、はるかに見えると振る舞います。
あなたはもともとExpectedException
属性を持っていなかったMSTestをし、使用している場合は、あなたがこれを行うことができます:
try
{
SomeExceptionThrowingMethod()
Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
Assert.IsTrue(ex is SpecificExceptionType);
}
あなたはNUnitのを使用している場合、あなたはこのような何かを行うことができます:
Assert.Throws<ExpectedException>(() => methodToTest());
さらにそれを検証するためにスローされた例外を格納することも可能です。
ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);
、ExpectedExceptionを使用しての注意が必要:
ます。http:// geekswithblogs。ネット/ sdorman /アーカイブ/ 2009/01/17 /ユニットテスト-と予想-exceptions.aspxする
そして、ここます:
http://xunit.github.io/docs/comparisons.htmlする
あなたは例外をテストする必要がある場合は、方法の際にあまり顔をしかめあります。あなたは、try {行為/失敗} ExpectedException以外の例外のテストを直接サポートしていないフレームワークのために役立つことができキャッチ{}のassertメソッドを使用することができます。
より良い代替手段は、他のすべての過ちから学び、改善された非常にモダン、楽しみ、かつ拡張ユニットテストフレームワークであるxUnit.NETを使用することです。そのような改善は、例外を主張するためのより良い構文を提供Assert.Throws、です。
あなたはgithubのでxUnit.NETを見つけることができます: http://xunit.github.io/する
私が取り組んでいるプロジェクトでは、これを行う別のソリューションがあります。
まず、ExpectedExceptionAttribute は例外を引き起こしたメソッド呼び出しを考慮するため、私は好きではありません。
代わりにヘルパーメソッドを使用してこれを行います。
テスト
[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
var file = File.Create("Accounts.bin");
file.WriteByte(1);
file.Close();
IAccountRepository repo = new FileAccountRepository();
TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());
}
ヘルパーメソッド
public static TException AssertThrows<TException>(Action action) where TException : Exception
{
try
{
action();
}
catch (TException ex)
{
return ex;
}
Assert.Fail("Expected exception was not thrown");
return null;
}
素敵ですね;)
MSTestを(v2)は次のように使用することができるAssert.ThrowsException機能を有している:
Assert.ThrowsException<System.FormatException>(() =>
{
Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
});
あなたはnugetでそれをインストールすることができます。Install-Package MSTest.TestFramework
それはあなたがアサートを使用していない...試験方法の属性です。このようになります。
[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()
以下を使用して Nuget からパッケージをダウンロードできます。 PM> インストールパッケージ MSTestExtensions それは追加します Assert.Throws() nUnit/xUnit のスタイルの構文を MsTest に適用します。
高レベルの指示:アセンブリをダウンロードして継承します ベーステスト そしてあなたは使うことができます Assert.Throws() 構文。
Throws 実装のメイン メソッドは次のようになります。
public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
try
{
task();
}
catch (Exception ex)
{
AssertExceptionType<T>(ex);
AssertExceptionMessage(ex, expectedMessage, options);
return;
}
if (typeof(T).Equals(new Exception().GetType()))
{
Assert.Fail("Expected exception but no exception was thrown.");
}
else
{
Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
}
}
開示:このパッケージをまとめてみました。
より詳しい情報: http://www.bradoncode.com/blog/2012/01/asserting-Exceptions-in-mstest-with.html
あなたは、単純な1行でこれを達成することができます。
あなたの操作foo.bar()
が非同期の場合:
await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());
foo.bar()
が非同期でない場合は、
Assert.ThrowsException<Exception>(() => foo.bar());
私はExpectedException属性を使用することはお勧めしませんか(それはあまりにも複雑で、エラーが発生しやすいですので)各テストでtry / catchブロックを記述すること。うまく設計されたAssertメソッドを使用します - いずれかのテストフレームワークが提供するか、独自に作成します。ここで私が書いたものだと使用しています。
public static class ExceptionAssert
{
private static T GetException<T>(Action action, string message="") where T : Exception
{
try
{
action();
}
catch (T exception)
{
return exception;
}
throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated. " + message);
}
public static void Propagates<T>(Action action) where T : Exception
{
Propagates<T>(action, "");
}
public static void Propagates<T>(Action action, string message) where T : Exception
{
GetException<T>(action, message);
}
public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
{
Propagates(action, validation, "");
}
public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
{
validation(GetException<T>(action, message));
}
}
例を使用します:
[TestMethod]
public void Run_PropagatesWin32Exception_ForInvalidExeFile()
{
(test setup that might propagate Win32Exception)
ExceptionAssert.Propagates<Win32Exception>(
() => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
(more asserts or something)
}
[TestMethod]
public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
{
(test setup that might propagate FileNotFoundException)
ExceptionAssert.Propagates<FileNotFoundException>(
() => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
e => StringAssert.Contains(e.Message, "NotThere.exe"));
(more asserts or something)
}
NOTES
それはやっので、他のは、私が使用して主張するよりも、このアサートの呼び出し構文は非常に違う除き、合理的なアイデアがされた例外を返す代わりに検証コールバックをサポートします。
他の人とは違って、私の代わりに、我々は唯一の例外は、呼び出しから伝播するかどうかをテストすることができますので、「スロー」の「伝播」を使用します。私たちは、例外がスローされていることを直接テストすることはできません。しかし、私は、あなたが画像を意味するスロー可能性とします。スローとキャッチされない。
FINAL THOUGHT
より多くの検証が必要とされた場合、テストは唯一の例外タイプを検証し、try / catchブロックを使用する場合にこの種のアプローチに切り替える前に、私はExpectedException属性を使用して検討しました。しかし、だけでなく、私は、各テストのために使用する技術について考えていますが、ニーズが変化して、他の1つの技術からのコードを変更することは些細な努力ではなかったでしょう。 1つの一貫したアプローチを使用すると、精神的な努力を節約できます。
だから、要約すると、このアプローチのスポーツ:使いやすさ、柔軟性と堅牢性(間違ったそれを行うのは難しい)。
上記@Richibanが提供するヘルパーは素晴らしい作品。
:その次のアドレスusing System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests
{
public static class MyAssert
{
/// <summary>
/// Helper for Asserting that a function throws an exception of a particular type.
/// </summary>
public static void Throws<T>( Action func ) where T : Exception
{
Exception exceptionOther = null;
var exceptionThrown = false;
try
{
func.Invoke();
}
catch ( T )
{
exceptionThrown = true;
}
catch (Exception e) {
exceptionOther = e;
}
if ( !exceptionThrown )
{
if (exceptionOther != null) {
throw new AssertFailedException(
String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
exceptionOther
);
}
throw new AssertFailedException(
String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
);
}
}
}
}
、ExpectedException
属性より良いオプションは Shoudly の」を使用することですS Should.Throwするます。
Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });
が がのためのを作成するために対処している必要があります。我々は要件の顧客を持っているとしましょうない場合は、CreateOrderForCustomer
方法はArgumentException
になるはずです。その後、我々は書くことができます:
[TestMethod]
public void NullUserIdInConstructor()
{
var customer = new Customer(name := "Justin", address := null};
Should.Throw<ArgumentException>(() => {
var order = CreateOrderForCustomer(customer) });
}
これは、私たちがエラーをスローしなければならないかについての具体的なされているためExpectedException
属性を使用するよりも優れています。これが私たちのテストで要件が明確になり、テストが失敗した場合にも、容易に診断を行うます。
非同期メソッドテスト用Should.ThrowAsync
もあることに注意してください。
別の方法として、あなたは実際にあなたのテストの次の2行でスローされているテストの例外を試すことができます。
var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);
さて、私よ、ここで誰もがとにかく、ここで私は良い答えに応じて構築されたコード:)すべてを行うために残されているが、コピーして使用されます...前に言ったことをかなりの合計アップ...
/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
try
{
methodToExecute();
}
catch (TException) {
return;
}
catch (System.Exception ex)
{
Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
}
Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
}
でVS内蔵のユニットテストあなたは、単に「例外」がスローされていることを確認したいのですが、あなたがキャッチを使用することができ、種類がわからない場合は、全てます:
[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
//...
}
チェック NUnitのドキュメントを約例についてを
[ExpectedException( typeof( ArgumentException ) )]
これは、あなたが使用しているテストフレームワークに依存するように起こっている?
MbUnitのでは、たとえば、あなたはあなたが本当に期待する例外を取得していることを保証するために、属性と予想される例外を指定することができます。
[ExpectedException(typeof(ArgumentException))]
使用する場合の のNUnitのは、これを試してください:
Assert.That(() =>
{
Your_Method_To_Test();
}, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));
これは古い質問ですが、私は議論に新しい考えを追加したいと思います。私は法、アサートアレンジ、予想されるパターンをアサートし、同法の整列を高めています。あなたは、それが割り当てられた主張、予想される例外ポインタを作ることができます。これはあなたのほとんどがちょうどテスト対象のメソッドを呼び出すためのコードの1行のためのあなたの行為の部分を残して、catchブロックでアサート行うよりもきれいな感じ。また、コード内の複数のポイントからAssert.Fail();
またはreturn
する必要はありません。それがキャッチされないため、そしてあなたの予想のタイプの例外がスローされた場合にスローされた他の例外は、テストが失敗する原因になりますが、それはメッセージまたはその他の特性に対してアサート、あなたが期待していたものではありませんでした例外は、あなたのテストが誤って合格しないことを確認に役立ちます。
[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
// Expectations
InvalidOperationException expectedException = null;
string expectedExceptionMessage = "Bar did something invalid.";
// Arrange
IDependency dependency = DependencyMocks.Create();
Foo foo = new Foo(dependency);
// Act
try
{
foo.Bar();
}
catch (InvalidOperationException ex)
{
expectedException = ex;
}
// Assert
Assert.IsNotNull(expectedException);
Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}
を NFluent に呼ばれる素晴らしいライブラリがありますスピードアップし、道あなたを容易にしますあなたのアサーションを記述の
これは、例外をスローするためにアサーションを記述するために非常に簡単です。
[Test]
public void given_when_then()
{
Check.ThatCode(() => MethodToTest())
.Throws<Exception>()
.WithMessage("Process has been failed");
}