XUnitを使用して例外をアサートする


111

私はXUnitとMoqの初心者です。文字列を引数として受け取るメソッドがあります。XUnitを使用して例外を処理する方法。

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    //act
    var result = profiles.GetSettingsForUserID("");
    //assert
    //The below statement is not working as expected.
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}

テスト中のメソッド

public IEnumerable<Setting> GetSettingsForUserID(string userid)
{            
    if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
    var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
    return s;
}

1
「期待どおりに機能していない」とはどういう意味ですか?(また、コードをより読みやすい形式にしてください。プレビューを使用し、読んでいた場合の外観に合わせて投稿してください。)
Jon Skeet

4
ヒント:を呼び出すGetSettingsForUserID("")前に電話をかけていAssert.Throwsます。Assert.Throwsコールがあなたを助けることはできません。AAAについてはあまり厳格ではないことをお勧めします...
ジョンスキート2017

回答:


183

Assert.Throwsの式は、例外をキャッチし、タイプをアサートします。ただし、assert式の外でテスト中のメソッドを呼び出しているため、テストケースに失敗しています。

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    // act & assert
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}

次のAAAにこだわる場合は、アクションを独自の変数に抽出できます。

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    //act
    Action act = () => profiles.GetSettingsForUserID("");
    //assert
    var exception = Assert.Throws<ArgumentException>(act);
    //The thrown exception can be used for even more detailed assertions.
    Assert.Equal("expected error message here", exception.Message);
}

例外がモード詳細アサーションにも使用できることに注意してください


5
非同期メソッドを使用している場合、Visual Studioは上記の構文で警告をスローします。それはこれを好む:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
アレック

5
実際、エラーが発生した私にとっては、「暗黙的にTaskをFunc <Task>に変換することはできません」。一方、私が置くだけではTask act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);それで満足し、正常に動作します。
アレック

非同期/待機での作業はこれにどのように影響しますか?テストでThrowsAsyncを使用してこれを実行しようとすると、エラーが正常にスローされてテストが終了するため、Assert.Equal行に到達しません。これが新しい質問になるかどうかを確認するために水をテストしています...
nathanjw

@AlecDenholmありがとう!それは私のために働いた唯一のものです。他の提案のいくつかは、非同期のものに対しては実際には適切に機能しないと思います。
商標

45

AAAについて厳格にしたい場合は、xUnitのRecord.Exceptionを使用して、Actステージで例外をキャプチャできます。

その後、アサートステージでキャプチャされた例外に基づいてアサーションを作成できます。

この例は、xUnitsテスト確認できます。

[Fact]
public void Exception()
{
    Action testCode = () => { throw new InvalidOperationException(); };

    var ex = Record.Exception(testCode);

    Assert.NotNull(ex);
    Assert.IsType<InvalidOperationException>(ex);
}

どのパスをたどるかはあなた次第であり、両方のパスはxUnitが提供するものによって完全にサポートされます。


1
FWIW、このソリューションは、例外メッセージなどを検証する必要がある場合に最適です。Record.Exceptionを使用するのはそのときだと思います。
Jeff LaFay

@JeffLaFay私がここのパーティーに少し遅れたことに感謝します。それを使用var exception = Assert.Throws<InvalidOperationException>(testCode);して主張することとどう違うのexception.Messageですか?それとも同じことを達成するための別の味ですか?
ColinM

3

AAAに固執する場合は、次のようなものを検討できます。

// Act 
Task act() => handler.Handle(request);

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.