これは例外をテストするのに良い方法だと思いますか?助言がありますか?
Exception exception = null;
try{
//I m sure that an exeption will happen here
}
catch (Exception ex){
exception = ex;
}
Assert.IsNotNull(exception);
MSテストを使用しています。
いくつかの異なるパターンを使用しています。例外が予想されるほとんどの場合、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 )
);
}
}
2017年には、新しい MSTest V2 Framework を使用して簡単に実行できます。
Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());
//async version
await Assert.ThrowsExceptionAsync<SomeException>(
() => myObject.SomeMethodAsync()
);
私はここに新しく、コメントや投票をするという評判はありませんが、 Andy Whiteの返事 の例の欠陥を指摘したかったのです。
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
sがあります。
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);
残念ながら、MSTest STILLには実際にExpectedException属性しかありません(MSがMSTestをどれだけ気にしているのかを示すだけです)。発生する。
MSTestを使用するために(/ forced by client)を使用しているときは、常にこのヘルパークラスを使用します。
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);
_
素敵で端正な?
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です
NUnit のクリーンなデリゲート構文を使用することをお勧めします。
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>);
}