Tegaskan Pengecualian menggunakan XUnit

111

Saya seorang pemula di XUnit dan Moq. Saya memiliki metode yang mengambil string sebagai argumen. Bagaimana menangani pengecualian menggunakan 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(""));
}

Metode sedang diuji

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;
}
wandermonk.dll
sumber
1
Apa yang Anda maksud dengan "tidak berfungsi seperti yang diharapkan"? (Selain itu, format kode Anda agar lebih mudah dibaca. Gunakan pratinjau, dan posting jika terlihat seperti yang Anda inginkan jika Anda membacanya.)
Jon Skeet
4
Petunjuk: Anda menelepon GetSettingsForUserID("")sebelum mulai menelepon Assert.Throws. The Assert.Throwspanggilan tidak dapat membantu Anda di sana. Saya sarankan untuk tidak terlalu kaku tentang AAA ...
Jon Skeet

Jawaban:

183

The Assert.Throws ekspresi akan menangkap pengecualian dan menegaskan jenis. Namun Anda memanggil metode yang sedang diuji di luar ekspresi assert dan karenanya gagal dalam kasus uji.

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

Jika bertekad mengikuti AAA, Anda dapat mengekstrak tindakan tersebut ke dalam variabelnya sendiri.

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

Perhatikan bagaimana pengecualian juga dapat digunakan untuk pernyataan detail mode

Nkosi
sumber
5
Jika menggunakan metode async, Visual Studio melontarkan peringatan dengan sintaks di atas. Ini lebih suka ini:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec
5
Sebenarnya bagi saya yang mengakibatkan error, 'tidak bisa secara implisit mengonversi Task ke Func <Task>', sedangkan jika saya taruh saja Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);maka itu senang dengan itu dan berfungsi dengan baik.
Alec
bagaimana bekerja dengan async / await memengaruhi hal ini? ketika saya mencoba melakukan ini menggunakan ThrowsAsync dalam pengujian saya, ia tidak pernah mencapai baris Assert.Equal karena berhasil melempar kesalahan dan berhenti dari pengujian. menguji air untuk melihat apakah ini harus menjadi pertanyaan baru ...
nathanjw
@AlecDenholm Terima kasih! Itulah satu-satunya hal yang berhasil bagi saya. Saya pikir beberapa saran lain tidak benar-benar berfungsi dengan baik untuk hal-hal asinkron.
merek dagang
45

Jika Anda ingin bersikap kaku tentang AAA, Anda dapat menggunakan Record.Exception dari xUnit untuk menangkap Exception dalam tahap Act Anda.

Anda kemudian dapat membuat pernyataan berdasarkan pengecualian yang ditangkap di tahap Assert.

Contohnya dapat dilihat di tes xUnits .

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

    var ex = Record.Exception(testCode);

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

Terserah Anda jalur mana yang ingin Anda ikuti, dan kedua jalur tersebut sepenuhnya didukung oleh apa yang disediakan xUnit.

Bhargav Rao
sumber
1
FWIW, Solusi ini sangat bagus jika Anda mungkin perlu memvalidasi pesan pengecualian, dll. Saya pikir saat itulah Anda mungkin menggunakan Record.Exception.
Jeff LaFay
@JeffLaFay Saya menghargai saya agak terlambat ke pesta di sini, apa bedanya dengan menggunakan var exception = Assert.Throws<InvalidOperationException>(testCode);dan menegaskan exception.Message? atau apakah itu hanya rasa lain untuk mencapai hal yang sama?
ColinM
3

Anda dapat mempertimbangkan sesuatu seperti ini jika Anda ingin tetap menggunakan AAA:

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

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
Yves Rochon
sumber