回答:
私はいくつかの異なるパターンを使用しています。ExpectedException
ほとんどの場合、例外が予想されるときにこの属性を使用します。ほとんどの場合これで十分ですが、これで十分でない場合もあります。例外はキャッチできない可能性があります-リフレクションによって呼び出されるメソッドによってスローされるため-または、トランザクションがロールバックされているか、値がまだ設定されているなど、他の条件が保持されていることを確認したいだけかもしれません。これらのケースtry/catch
では、正確な例外を期待するブロックにラップしAssert.Fail
、コードが成功した場合はを実行し、一般的な例外をキャッチして、別の例外がスローされないようにします。
最初のケース:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MethodTest()
{
var obj = new ClassRequiringNonNullParameter( null );
}
2番目のケース:
[TestMethod]
public void MethodTest()
{
try
{
var obj = new ClassRequiringNonNullParameter( null );
Assert.Fail("An exception should have been thrown");
}
catch (ArgumentNullException ae)
{
Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message );
}
catch (Exception e)
{
Assert.Fail(
string.Format( "Unexpected exception of type {0} caught: {1}",
e.GetType(), e.Message )
);
}
}
Assert.Throws
はこれらの両方のケースをカバーする強く型付けされたメソッドを持つxUnitの使用に移りました 。
2017年、新しいMSTest V2フレームワークを使用して、より簡単に行うことができます。
Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());
//async version
await Assert.ThrowsExceptionAsync<SomeException>(
() => myObject.SomeMethodAsync()
);
System.Exception
がスローされた場合にのみ成功します。それ以外はSystem.ArgumentException
、テストに失敗します。
Assert.ThrowsException<MyException>
、提供された例外タイプに対してのみテストを行うものであり、派生した例外タイプに対してはテストしないことです。私の例では、試験された場合Sub
であった(基本クラスから派生型)、テストはだろう失敗します。Throw
MyInheritedException
MyException
Try { SubToTest(); Assert.Fail("...") } Catch (AssertFailedException e) {throw;} Catch (MyException e) {...}
ます。Catch (AssertFailedException e) {throw;}
(algeekからのコメントを参照)の最も重要性に注意してください
私はここに新しく、コメントしたり、反対票を投じるという評判はありませんが、アンディホワイトの返信の例の欠陥を指摘したいと思います。
try
{
SomethingThatCausesAnException();
Assert.Fail("Should have exceptioned above!");
}
catch (Exception ex)
{
// whatever logging code
}
私が精通しているすべての単体テストフレームワークではAssert.Fail
、例外をスローすることで機能するため、汎用キャッチは実際にテストの失敗を隠蔽します。SomethingThatCausesAnException()
がスローされない場合、Assert.Fail
意志はありますが、失敗を示すためにテストランナーにバブルアウトすることはありません。
予期される例外をキャッチする必要がある場合(つまり、例外のメッセージ/プロパティなどの特定の詳細をアサートする場合)、基本のExceptionクラスではなく、特定の予期されるタイプをキャッチすることが重要です。これにより、Assert.Fail
例外が発生します(ユニットテストフレームワークが行うのと同じタイプの例外をスローしていない場合)、SomethingThatCausesAnException()
メソッドによってスローされた例外の検証は可能です。
v 2.5以降、NUnitには、Assert
例外をテストするための次のメソッドレベルのがあります。
Assert.Throws。正確な例外タイプをテストします。
Assert.Throws<NullReferenceException>(() => someNullObject.ToString());
そしてAssert.Catch
、指定されたタイプの例外、またはこのタイプから派生した例外タイプをテストします。
Assert.Catch<Exception>(() => someNullObject.ToString());
余談ですが、例外をスローする単体テストをデバッグするときは、VS が例外でブレークしないようにする必要があります。
編集する
ただ、一般的なのリターンは、以下のマシューさんのコメントの例を与える Assert.Throws
と、Assert.Catch
あなたはその後、さらに検査のために調べることができる例外の種類を持つ例外です。
// The type of ex is that of the generic type parameter (SqlException)
var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks());
Assert.AreEqual(1205, ex.Number);
Assert.Throws
あなたが例外自体にさらなるアサーションを書くことができるように加えて、それは例外を返します。
残念ながら、MSTestは実際にはExpectedException属性しか持っていません(MSがMSTestを気にかけている程度を示しているだけです)。これは、IMOがArrange / Act / Assertパターンを壊し、例外を予期するコード行を正確に指定できないためです。発生します。
MSTestを使用するために(クライアントによって/ forced)使用しているときは、常にこのヘルパークラスを使用します。
public static class AssertException
{
public static void Throws<TException>(Action action) where TException : Exception
{
try
{
action();
}
catch (Exception ex)
{
Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
return;
}
Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
}
public static void Throws<TException>(Action action, string expectedMessage) where TException : Exception
{
try
{
action();
}
catch (Exception ex)
{
Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
Assert.AreEqual(expectedMessage, ex.Message, "Expected exception with a message of '" + expectedMessage + "' but exception with message of '" + ex.Message + "' was thrown instead.");
return;
}
Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
}
}
使用例:
AssertException.Throws<ArgumentNullException>(() => classUnderTest.GetCustomer(null));
ExpectedException
属性を使用する代わりに、テストクラスに役立つ2つのメソッドを定義することがあります。
AssertThrowsException()
デリゲートを受け取り、予想されるメッセージを伴う予想される例外をスローすることを表明します。
AssertDoesNotThrowException()
同じデリゲートを受け取り、例外をスローしないことを表明します。
このペアリングは、例外がスローされたが、他のケースではスローされないことをテストする場合に非常に役立ちます。
それらを使用すると、私の単体テストコードは次のようになります。
ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); };
// Check exception is thrown correctly...
AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready.");
testObj.Ready = true;
// Check exception is now not thrown...
AssertDoesNotThrowException(callStartOp);
素敵で端正なの?
my AssertThrowsException()
およびAssertDoesNotThrowException()
メソッドは、次のように共通の基本クラスで定義されています。
protected delegate void ExceptionThrower();
/// <summary>
/// Asserts that calling a method results in an exception of the stated type with the stated message.
/// </summary>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
/// <param name="expectedExceptionType">The expected type of the exception, e.g. typeof(FormatException).</param>
/// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param>
protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage)
{
try
{
exceptionThrowingFunc();
Assert.Fail("Call did not raise any exception, but one was expected.");
}
catch (NUnit.Framework.AssertionException)
{
// Ignore and rethrow NUnit exception
throw;
}
catch (Exception ex)
{
Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type.");
Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=\"" + expectedExceptionMessage + "\", got \"" + ex.Message + "\"");
}
}
/// <summary>
/// Asserts that calling a method does not throw an exception.
/// </summary>
/// <remarks>
/// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (e.g. once you have tested that an ExceptionThrower
/// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed).
/// </remarks>
/// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc)
{
try
{
exceptionThrowingFunc();
}
catch (NUnit.Framework.AssertionException)
{
// Ignore and rethrow any NUnit exception
throw;
}
catch (Exception ex)
{
Assert.Fail("Call raised an unexpected exception: " + ex.Message);
}
}
ExpectedExceptionAttributeでテストをマークします(これはNUnitまたはMSTestの用語です。他のユニットテストフレームワークのユーザーは翻訳する必要がある場合があります)。
ほとんどの.netユニットテストフレームワークでは、テストメソッドに[ExpectedException]属性を設定できます。ただし、これでは、予期した時点で例外が発生したことがわかりません。ここでxunit.netが役立ちます。
xunitにはAssert.Throwsがあるので、次のようなことができます。
[Fact]
public void CantDecrementBasketLineQuantityBelowZero()
{
var o = new Basket();
var p = new Product {Id = 1, NetPrice = 23.45m};
o.AddProduct(p, 1);
Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3));
}
[Fact]は[TestMethod]に相当するxunitです
テストの例ArgumentNullExeption
:
[Test]
[TestCase(null)]
public void FooCalculation_InvalidInput_ShouldThrowArgumentNullExeption(string text)
{
var foo = new Foo();
Assert.That(() => foo.Calculate(text), Throws.ArgumentNullExeption);
//Or:
Assert.That(() => foo.Calculate(text), Throws.Exception.TypeOf<ArgumentNullExeption>);
}