web-dev-qa-db-ja.com

Assertで例外をテストして、スローされることを確認する最良の方法

これは例外をテストするのに良い方法だと思いますか?助言がありますか?

Exception exception = null;
try{
    //I m sure that an exeption will happen here
}
catch (Exception ex){
    exception = ex;
}

Assert.IsNotNull(exception);

MSテストを使用しています。

82
Hannoun Yassir

いくつかの異なるパターンを使用しています。例外が予想されるほとんどの場合、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 )
        );
    }
}
127
tvanfosson

2017年には、新しい MSTest V2 Framework を使用して簡単に実行できます。

Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());

//async version
await Assert.ThrowsExceptionAsync<SomeException>(
  () => myObject.SomeMethodAsync()
);
30
Icaro Bombonato

私はここに新しく、コメントや投票をするという評判はありませんが、 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() 方法。

17
allgeek

V 2.5、NUnit には、例外をテストするための次のメソッドレベルAssertsがあります。

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);
15
StuartLC

残念ながら、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));
11
bytedev

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);
    }
}
_
9
GrahamS

ExpectedExceptionAttributeでテストをマークします(これはNUnitまたはMSTestの用語です。他のユニットテストフレームワークのユーザーは翻訳が必要な場合があります)。

4
itowlson

ほとんどの.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です

3
Steve Willcock

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>);
}
0
Shahar Shokrani