45

[編集(2020年5月)] -この問題は、NUnitの新しいリリースで対処されたと報告されています。Nunit.ThrowsAsyncを参照してください。(この回答を参照してください、@ James-Rossに感謝します)


UserControllerこのアクションのコントローラーがあります

// GET /blah
public Task<User> Get(string domainUserName)
{
        if (string.IsNullOrEmpty(domainUserName))
        {
            throw new ArgumentException("No username specified.");
        }

        return Task.Factory.StartNew(
            () =>
                {
                    var user = userRepository.GetByUserName(domainUserName);
                    if (user != null)
                    {
                        return user;
                    }

                    throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("{0} - username does not exist", domainUserName)));
                });
}

404例外をスローした場合のテストを作成しようとしています。

これが私が試したもので、出力があります-

1)

[Test]
public void someTest()
{
        var mockUserRepository = new Mock<IUserRepository>();
        mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
    var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };

    Assert.That(async () => await userController.Get("foo"), Throws.InstanceOf<HttpResponseException>());
}

結果 テストに失敗しました

  Expected: instance of <System.Web.Http.HttpResponseException>
  But was:  no exception thrown
  1. [テスト]publicvoid someTest(){var mockUserRepository = new Mock(); mockUserRepository.Setup(x => x.GetByUserName(It.IsAny()))。Returns(default(User)); var userController = new UserController(mockUserRepository.Object){Request = new HttpRequestMessage()};

      var httpResponseException = Assert.Throws<HttpResponseException>(() => userController.Get("foo").Wait());
      Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
    

    }

結果 テストに失敗しました

  Expected: <System.Web.Http.HttpResponseException>
  But was:  <System.AggregateException> (One or more errors occurred.)
[Test]
public void someTest()
{
        var mockUserRepository = new Mock<IUserRepository>();
        mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
    var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };

    var httpResponseException = Assert.Throws<HttpResponseException>(async () => await userController.Get("foo"));
    Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}

結果 テストに失敗しました

  Expected: <System.Web.Http.HttpResponseException>
  But was:  null
[Test]
[ExpectedException(typeof(HttpResponseException))]
public async void ShouldThrow404WhenNotFound()
{            var mockUserRepository = new Mock<IUserRepository>();
        mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));

    var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };

    var task = await userController.Get("foo");
}

結果 テストに合格

質問-

  1. ExpectedExceptionが処理するのに、Assert.ThrowsがHttpResponseExceptionを処理しないのはなぜですか?
  2. 例外がスローされることをテストしたくありません。応答のステータスコードを主張したい。これを行う方法は何ですか?

これらの動作とその原因についての比較は素晴らしいでしょう!

4

6 に答える 6

57

が原因で問題が発生していasync voidます。

特に:

  1. async () => await userController.Get("foo")はに変換されTestDelegate、はを返すvoidため、ラムダ式はとして扱われasync voidます。したがって、テストランナーはラムダの実行を開始しますが、ラムダが完了するのを待ちません。ラムダはGet(それがであるため)完了する前に戻りasync、テストランナーは例外なく戻ったことを確認します。

  2. Wait例外を。でラップしますAggregateException

  3. この場合も、asyncラムダはとして扱われるasync voidため、テストランナーはその完了を待機していません。

  4. async Taskではなくこれを作成することをお勧めしますasync voidが、この場合、テストランナーは完了を待機するため、例外が発生します。

このバグレポートによると、NUnitの次のビルドでこれに対する修正があります。それまでの間、独自のThrowsAsyncメソッドを作成できます。xUnitの例はここにあります

于 2013-03-26T12:13:18.887 に答える
50

いつ追加されたかはわかりませんが、現在のバージョンのNunit(執筆時点では3.4.1)にはThrowsAsyncメソッドが含まれています

https://github.com/nunit/docs/wiki/Assert.ThrowsAsyncを参照してください

例:

[Test]
public void ShouldThrow404WhenNotFound()
{
    var mockUserRepository = new Mock<IUserRepository>();
    mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
    var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };

    var exception = Assert.ThrowsAsync<HttpResponseException>(() => userController.Get("foo"));

    Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
于 2016-10-13T21:05:18.680 に答える
12

このブログは私のような問題について話します。

私はそこで提案された推奨事項に従い、このようなテストを行いました-

    [Test]
    public void ShouldThrow404WhenNotFound()
    {
        var mockUserRepository = new Mock<IUserRepository>();
        mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
        var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };

        var aggregateException = Assert.Throws<AggregateException>(() => userController.Get("foo").Wait());
        var httpResponseException = aggregateException.InnerExceptions
            .FirstOrDefault(x => x.GetType() == typeof(HttpResponseException)) as HttpResponseException;

        Assert.That(httpResponseException, Is.Not.Null);
        Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
    }

私はあまり満足していませんが、これは機能します。

編集1

@StephenClearyに触発されて、探しているアサートを実行する静的ヘルパークラスを追加しました。このように見えます-

public static class AssertEx
{
    public static async Task ThrowsAsync<TException>(Func<Task> func) where TException : class
    {
        await ThrowsAsync<TException>(func, exception => { });
    } 

    public static async Task ThrowsAsync<TException>(Func<Task> func, Action<TException> action) where TException : class
    {
        var exception = default(TException);
        var expected = typeof(TException);
        Type actual = null;
        try
        {
            await func();
        }
        catch (Exception e)
        {
            exception = e as TException;
            actual = e.GetType();
        }

        Assert.AreEqual(expected, actual);
        action(exception);
    }
}

私は今、次のようなテストを行うことができます-

    [Test]
    public async void ShouldThrow404WhenNotFound()
    {
        var mockUserRepository = new Mock<IUserRepository>();
        mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
        var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };

        Action<HttpResponseException> asserts = exception => Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
        await AssertEx.ThrowsAsync(() => userController.Get("foo"), asserts);
    }
于 2013-03-26T12:09:54.597 に答える
3

タスクを待つ場合、スローされた例外はAggregateExceptionに集約されます。AggregateExceptionの内部例外を検査できます。これが、ケース2が機能しない理由である可能性があります。

タスク内で実行されているユーザーコードによってスローされた未処理の例外は、このトピックで後述する特定のシナリオを除いて、参加しているスレッドに伝播されます。静的メソッドまたはインスタンスTask.WaitメソッドまたはTask.Waitメソッドのいずれかを使用し、呼び出しをtry-catchステートメントで囲むことによって例外を処理すると、例外が伝播されます。タスクがアタッチされた子タスクの親である場合、または複数のタスクを待機している場合は、複数の例外がスローされる可能性があります。すべての例外を呼び出し元のスレッドに伝播するために、タスクインフラストラクチャはそれらをAggregateExceptionインスタンスにラップします。AggregateExceptionにはInnerExceptionsプロパティがあり、これを列挙して、スローされたすべての元の例外を調べ、それぞれを個別に処理する(または処理しない)ことができます。例外が1つだけスローされた場合でも、

MSDNへのリンク

于 2013-03-26T10:53:42.060 に答える
2

シナリオ3で発生したのと同様の問題があります。次の結果が原因でテストケースが失敗しました

Expected: <UserDefineException>
But was:  null

Assert.ThrowAsync<>を使用することで問題が解決します

私のWebAPIアクションメソッドとユニットテストケースメソッドは次のとおりです

public async Task<IHttpActionResult> ActionMethod(RequestModel requestModel)
{
   throw UserDefineException();
}


[Test]
public void Test_Contrller_Method()
{
   Assert.ThrowsAsync<UserDefineException>(() => _controller.ActionMethod(new RequestModel()));
}    
于 2018-05-25T06:22:18.350 に答える
1

これはドキュメントの例です。

var ex = Assert.ThrowsAsync<ArgumentException>(async () => await MethodThatThrows());
  1. 使用するThrowsAsync
  2. 使用async/await

https://docs.nunit.org/articles/nunit/writing-tests/assertions/classic-assertions/Assert.ThrowsAsync.html

于 2020-08-22T02:43:24.203 に答える