911

Assert(または他の Test クラスを使用して) 例外がスローされたことを確認するにはどうすればよいですか?

4

24 に答える 24

1056

「Visual Studio Team Test」の場合、ExpectedException 属性をテストのメソッドに適用しているようです。

ドキュメントのサンプル: Visual Studio Team Test を使用した単体テストのチュートリアル

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}
于 2009-06-01T05:09:23.320 に答える
273

通常、テスト フレームワークにはこれに対する答えがあります。ただし、柔軟性が十分でない場合は、いつでもこれを行うことができます。

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();
}
于 2009-06-01T05:06:54.727 に答える
123

これを実装するための私の推奨方法は、Throws というメソッドを作成し、それを他の 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());
}

これは、他の単体テスト構文によく似た外観と動作をします。

于 2011-04-12T11:20:45.543 に答える
94

NUNIT を使用すると、次のようなことができます。

Assert.Throws<ExpectedException>(() => methodToTest());


さらに検証するために、スローされた例外を保存することもできます。

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

参照: http://nunit.org/docs/2.5/exceptionAsserts.html

于 2013-12-04T11:02:29.527 に答える
63

元々属性がなかった MSTest を使用している場合は、次のExpectedExceptionようにできます。

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}
于 2009-06-01T05:10:19.323 に答える
53

MSTest (v2) には、次のように使用できる Assert.ThrowsException 関数があります。

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            }); 

ナゲットでインストールできます:Install-Package MSTest.TestFramework

于 2016-10-18T16:03:10.947 に答える
36

ここに示すように、いくつかの落とし穴につながる可能性があるため、ExpectedException の使用には注意してください。

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

そしてここ:

http://xunit.github.io/docs/comparisons.html

例外をテストする必要がある場合、あまり嫌われない方法があります。メソッドを使用できますtry{act/fail}catch{assert}。これは、.NET 以外の例外テストを直接サポートしていないフレームワークに役立ちますExpectedException

より良い代替手段は、xUnit.NET を使用することです。xUnit.NET は、他のすべての過ちから学び、改善された、非常にモダンで将来を見据えた拡張可能な単体テスト フレームワークです。そのような改善の 1 つが ですAssert.Throws。これは、例外をアサートするためのより優れた構文を提供します。

xUnit.NET は github にあります: http://xunit.github.io/

于 2009-06-01T05:22:42.443 に答える
24

私が取り組んでいるプロジェクトでは、これを行う別のソリューションがあります。

まず、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;
    }

いいですね;)

于 2010-10-01T21:13:12.910 に答える
15

テストメソッドの属性です...アサートは使用しません。次のようになります。

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()
于 2009-06-01T05:09:18.700 に答える
13

nUnit/xUnit のスタイルでAssert.Throws()構文を MsTestに追加するPM> Install-Package MSTestExtensionsを使用して、Nuget からパッケージをダウンロードできます。

大まかな手順: アセンブリをダウンロードしてBaseTestから継承すると、 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

于 2015-04-07T23:11:14.127 に答える
5

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)
    }

ノート

検証コールバックをサポートする代わりに例外を返すことは、この assert の呼び出し構文が私が使用する他の assert とは大きく異なることを除けば、妥当な考えです。

他とは異なり、例外が呼び出しから伝播するかどうかしかテストできないため、「スロー」の代わりに「伝播」を使用します。例外がスローされることを直接テストすることはできません。しかし、投げるということは、「投げられて捕まらない」という意味でイメージできると思います。

最終的な考え

この種のアプローチに切り替える前に、テストで例外の種類のみを検証する場合は ExpectedException 属性を使用し、さらに検証が必要な場合は try/catch ブロックを使用することを検討しました。しかし、テストごとにどの手法を使用するかを考えなければならないだけでなく、ニーズの変化に応じてコードをある手法から別の手法に変更することは簡単ではありませんでした。1 つの一貫したアプローチを使用することで、精神的な労力を節約できます。

要約すると、このアプローチは、使いやすさ、柔軟性、および堅牢性 (間違いにくい) を備えています。

于 2014-08-01T16:14:06.280 に答える
4

さて、私はここにいる他のみんなが以前に言ったことをかなり要約します...とにかく、ここに私が良い答えに従って構築したコードがあります:)あとはコピーして使用するだけです...

/// <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.");  
}
于 2012-09-14T20:12:52.450 に答える
4

上記の@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))
                    );
            }
        }
    }
}
于 2013-04-05T00:02:33.393 に答える
4

別の方法として、テストの次の 2 行で実際に例外がスローされていることをテストしてみてください。

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);
于 2016-12-14T12:17:14.673 に答える
2

これは、使用しているテスト フレームワークによって異なりますか?

たとえば、MbUnit では、予想される例外を属性で指定して、実際に予想される例外が確実に発生するようにすることができます。

[ExpectedException(typeof(ArgumentException))]
于 2009-06-01T05:09:59.667 に答える
2

次の例については、nUnit ドキュメントをご覧ください。

[ExpectedException( typeof( ArgumentException ) )]
于 2009-06-01T05:10:14.270 に答える
1

これは古い質問ですが、議論に新しい考えを加えたいと思います。Arrange、Act、Assert パターンを Expect、Arrange、Act、Assert に拡張しました。予想される例外ポインターを作成し、それが割り当てられたことをアサートできます。これは、catch ブロックで Assert を実行するよりもすっきりしているように感じられ、Act セクションは、テスト対象のメソッドを呼び出す 1 行のコードだけに残します。また、コード内の複数のポイントに出入りする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);
}
于 2015-09-11T19:56:49.020 に答える