Bagaimana cara menggunakan Assert.Throws untuk menegaskan jenis pengecualian?


246

Bagaimana cara saya gunakan Assert.Throwsuntuk menegaskan jenis pengecualian dan kata-kata aktual pesan.

Sesuatu seperti ini:

Assert.Throws<Exception>(
    ()=>user.MakeUserActive()).WithMessage("Actual exception message")

Metode yang saya uji melempar banyak pesan dari jenis yang sama, dengan pesan yang berbeda, dan saya perlu cara untuk menguji apakah pesan yang benar dilemparkan tergantung pada konteksnya.

Jawaban:


443

Assert.Throws mengembalikan pengecualian yang dilemparkan yang memungkinkan Anda menegaskan pengecualian.

var ex = Assert.Throws<Exception>(() => user.MakeUserActive());
Assert.That(ex.Message, Is.EqualTo("Actual exception message"));

Jadi, jika tidak ada pengecualian yang dilemparkan, atau pengecualian dari tipe yang salah dilemparkan, Assert.Throwspernyataan pertama akan gagal. Namun, jika pengecualian dari jenis yang benar dilempar maka Anda sekarang dapat menegaskan pada pengecualian yang sebenarnya Anda simpan dalam variabel.

Dengan menggunakan pola ini Anda dapat menegaskan hal-hal lain selain pesan pengecualian, misalnya dalam kasus ArgumentExceptiondan turunannya, Anda dapat menegaskan bahwa nama parameter sudah benar:

var ex = Assert.Throws<ArgumentNullException>(() => foo.Bar(null));
Assert.That(ex.ParamName, Is.EqualTo("bar"));

Anda juga dapat menggunakan API yang lancar untuk melakukan pernyataan ini:

Assert.That(() => foo.Bar(null), 
Throws.Exception
  .TypeOf<ArgumentNullException>()
  .With.Property("ParamName")
  .EqualTo("bar"));

atau sebagai alternatif

Assert.That(
    Assert.Throws<ArgumentNullException>(() =>
        foo.Bar(null)
    .ParamName,
Is.EqualTo("bar"));

Sedikit tip ketika menegaskan pada pesan pengecualian adalah untuk menghias metode pengujian dengan SetCultureAttributeuntuk memastikan bahwa pesan yang dilemparkan menggunakan budaya yang diharapkan. Ini mulai berlaku jika Anda menyimpan pesan pengecualian sebagai sumber daya untuk memungkinkan pelokalan.


Ini sangat membantu bagi saya - saya ingin cara untuk menampilkan kesalahan, saya bahkan tidak membaca jika nilai dikembalikan oleh metode Assert.Throws. Terima kasih
Haroon

6
+1 Terima kasih telah menunjukkan Fluent API, untuk beberapa alasan saya mengalami masalah dalam memahami cara menggunakannya hanya dari dokumen NUnit saja.
aolszowka

Saat Anda ingin menegaskan pesan, Anda juga dapat langsung menggunakan properti Pesan alih-alih "Properti".
Marcel

25

Anda sekarang dapat menggunakan ExpectedExceptionatribut, mis

[Test]
[ExpectedException(typeof(InvalidOperationException), 
 ExpectedMessage="You can't do that!"]
public void MethodA_WithNull_ThrowsInvalidOperationException()
{
    MethodA(null);
}

2
Ini sedikit mengganggu saya ketika pertama kali dilihat, karena tes jelas tidak menegaskan, yang merupakan bau bagi saya. Ini adalah fitur yang bagus, tetapi orang harus berdiskusi di tim mana atribut ini harus digunakan selama Assert. Throws
Marcel

14
+1 juga merupakan cara yang bagus untuk menguji pengecualian. Satu-satunya hal yang perlu diingat tentang ini adalah bahwa secara teoritis setiap baris kode yang melempar InvalidOperationException dengan pesan itu akan lulus tes, termasuk kode dalam tes Anda yang menyiapkan data uji / objek atau metode lain yang mungkin perlu Anda lakukan sebelum yang Anda tertarik untuk menguji, mungkin menghasilkan false positive. Tentu saja, itu tergantung seberapa spesifik pesan itu dan jenis pengecualian yang Anda uji. Dengan Assert.ThrowAnda dapat menargetkan garis tepat yang Anda minati.
Tidak

21
Atribut ExpectedException sudah tidak digunakan lagi di NUnit 3: github.com/nunit/docs/wiki/Breaking-Changes
Frank Sebastià

13
Assert.That(myTestDelegate, Throws.ArgumentException
    .With.Property("Message").EqualTo("your argument is invalid."));

2
Dengan diperkenalkannya nama operator, saya akan mengedit server yang bagus ini untuk:Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
Samuel

@Amuel Edit itu akan menggunakan referensi yang sangat diketik yang bagus, tetapi di sisi lain, itu adalah nama properti yang sangat rendah churn dan string sihir meningkatkan kelancaran. Soal rasa kurasa
Jordan Morris

1
Saya sepenuhnya setuju dengan Anda tentang Exception.Message. Saya masih akan merekomendasikan untuk setidaknya menambahkan alternatif ini karena With.Propertydapat digunakan pada objek lain juga, yang mana kasus ini akan meningkatkan stabilitas kode.
Samuel

5

Ini adalah pertanyaan lama namun relevan dengan jawaban yang sudah usang jadi saya menambahkan solusi saat ini:

public void Test() {
    throw new MyCustomException("You can't do that!");
}

[TestMethod]
public void ThisWillPassIfExceptionThrown()
{
    var exception = Assert.ThrowsException<MyCustomException>(
        () => Test(),
        "This should have thrown!");
    Assert.AreEqual("You can't do that!", exception.Message);
}

Ini bekerja dengan using Microsoft.VisualStudio.TestTools.UnitTesting;


Saya benar-benar terkejut bahwa tidak ada cara ringkas untuk menyatakan bahwa metode melempar pengecualian seperti di JUnit. Kecuali ada implikasi yang tidak saya sadari, ini mungkin jawaban yang paling relevan saat ini.
NetherGranite

3

Untuk memperluas jawaban persisten, dan untuk memberikan lebih banyak fungsi NUnit, Anda dapat melakukan ini:

public bool AssertThrows<TException>(
    Action action,
    Func<TException, bool> exceptionCondition = null)
    where TException : Exception 
{
    try
    {
        action();
    }
    catch (TException ex)
    {
        if (exceptionCondition != null)
        {
            return exceptionCondition(ex);
        }

        return true;
    }
    catch
    {
        return false;
    }

    return false; 
}

Contoh:

// No exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => {}));

// Wrong exception thrown - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new ApplicationException(); }));

// Correct exception thrown - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException(); }));

// Correct exception thrown, but wrong message - test fails.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("ABCD"); },
        ex => ex.Message == "1234"));

// Correct exception thrown, with correct message - test passes.
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
        () => { throw new InvalidOperationException("1234"); },
        ex => ex.Message == "1234"));

2

Ini sudah lama sejak masalah ini diangkat, saya sadar, tetapi saya baru-baru ini mengalami hal yang sama, dan menyarankan fungsi ini untuk MSTest:

public bool AssertThrows(Action action) where T : Exception 
{ 
try {action();} 
catch(Exception exception) 
{ 
    if (exception.GetType() == typeof(T)) return true; 
} 
return false; 
}

pemakaian:

Assert.IsTrue(AssertThrows<FormatException>(delegate{ newMyMethod(MyParameter); }));

Lebih lanjut di sini: http://phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-occured/


2

Karena saya terganggu oleh verbositas dari beberapa pola NUnit baru, saya menggunakan sesuatu seperti ini untuk membuat kode yang lebih bersih bagi saya secara pribadi:

public void AssertBusinessRuleException(TestDelegate code, string expectedMessage)
{
    var ex = Assert.Throws<BusinessRuleException>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

public void AssertException<T>(TestDelegate code, string expectedMessage) where T:Exception
{
    var ex = Assert.Throws<T>(code);
    Assert.AreEqual(ex.Message, expectedMessage);
}

Maka penggunaannya adalah:

AssertBusinessRuleException(() => user.MakeUserActive(), "Actual exception message");

1
Apa itu TestDelegate?
reggaeguitar

1
Ini memungkinkan Anda untuk melewatkan kode untuk dieksekusi sebagai parameter. Ini adalah kelas dalam kerangka NUnit (v3.2.0.0).
Savage
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.