Làm cách nào để sử dụng Assert.Throws để xác nhận loại ngoại lệ?


246

Làm cách nào để sử dụng Assert.Throwsđể xác nhận loại ngoại lệ và từ ngữ thông điệp thực tế.

Một cái gì đó như thế này:

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

Phương pháp tôi đang kiểm tra ném nhiều tin nhắn cùng loại, với các tin nhắn khác nhau và tôi cần một cách để kiểm tra rằng tin nhắn chính xác được ném tùy thuộc vào ngữ cảnh.

Câu trả lời:


443

Assert.Throws trả về ngoại lệ được ném cho phép bạn xác nhận ngoại lệ.

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

Vì vậy, nếu không có ngoại lệ được ném, hoặc một ngoại lệ của loại sai được ném, Assert.Throwskhẳng định đầu tiên sẽ thất bại. Tuy nhiên, nếu một ngoại lệ của loại chính xác được ném thì bây giờ bạn có thể xác nhận về ngoại lệ thực tế mà bạn đã lưu trong biến.

Bằng cách sử dụng mẫu này, bạn có thể khẳng định những thứ khác ngoài thông báo ngoại lệ, ví dụ trong trường hợp ArgumentExceptionvà các dẫn xuất, bạn có thể khẳng định rằng tên tham số là chính xác:

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

Bạn cũng có thể sử dụng API thông thạo để thực hiện các xác nhận sau:

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

Hay cách khác

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

Một mẹo nhỏ khi khẳng định thông điệp ngoại lệ là trang trí phương thức kiểm tra với SetCultureAttributeđể đảm bảo rằng thông điệp ném được sử dụng văn hóa dự kiến. Điều này phát huy tác dụng nếu bạn lưu trữ các thông điệp ngoại lệ của mình dưới dạng tài nguyên để cho phép bản địa hóa.


Điều này thực sự hữu ích cho tôi - Tôi muốn có một cách để hiển thị lỗi, tôi thậm chí không đọc nếu một giá trị được trả về bởi phương thức Assert.Throws. Cảm ơn
Haroon

6
+1 Cảm ơn bạn đã hiển thị API thông thạo, vì một số lý do tôi gặp vấn đề khi hiểu cách sử dụng nó chỉ từ các tài liệu NUnit.
aolszowka

Khi bạn muốn xác nhận tin nhắn, bạn cũng có thể trực tiếp sử dụng thuộc tính Tin nhắn thay vì "Thuộc tính".
Marcel

25

Bây giờ bạn có thể sử dụng các ExpectedExceptionthuộc tính, ví dụ

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

2
Điều này làm tôi băn khoăn một chút khi lần đầu tiên nhìn thấy, bởi vì bài kiểm tra rõ ràng không có xác nhận, đó là một mùi đối với tôi. Đây là một tính năng hay, nhưng người ta nên thảo luận trong nhóm người mà sự quy kết này sẽ được sử dụng qua Assert.Th Ném
Marcel

14
+1 cũng là một cách hay để kiểm tra ngoại lệ. Điều duy nhất cần lưu ý về điều này là về mặt lý thuyết, bất kỳ dòng mã nào ném UnlimitedOperationException với thông báo đó sẽ vượt qua bài kiểm tra, bao gồm mã trong bài kiểm tra của bạn để chuẩn bị dữ liệu / đối tượng thử nghiệm hoặc bất kỳ phương thức nào khác bạn có thể cần thực hiện trước khi một trong những bạn quan tâm đến thử nghiệm, có thể dẫn đến kết quả dương tính giả. Dĩ nhiên, điều đó phụ thuộc vào mức độ cụ thể của thông điệp và loại ngoại lệ bạn đang kiểm tra. Với Assert.Throwbạn có thể nhắm mục tiêu chính xác dòng bạn quan tâm.
Nope

21
Thuộc tính ExpectedException không được dùng nữa trong NUnit 3: github.com/nunit/docs/wiki/Breaking- Thay đổi
Frank Sebastià

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

2
Với sự giới thiệu của toán tử nameof, tôi sẽ chỉnh sửa anwser tuyệt vời này thành:Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
Samuel

@Samuel Bản chỉnh sửa đó sẽ sử dụng một tài liệu tham khảo được gõ mạnh, rất hay, nhưng mặt khác, nó là một tên thuộc tính cực kỳ thấp và chuỗi ma thuật giúp cải thiện sự lưu loát. Một vấn đề về hương vị tôi cho rằng
Jordan Morris

1
Tôi hoàn toàn đồng ý với bạn về Exception.Message. Tôi vẫn sẽ khuyên bạn ít nhất nên thêm sự thay thế này bởi vì With.Propertycó thể được sử dụng trên các đối tượng khác, những trường hợp này sẽ cải thiện tính ổn định của mã.
Samuel

5

Đây là một câu hỏi cũ nhưng có liên quan với các câu trả lời đã lỗi thời vì vậy tôi đang thêm giải pháp hiện tại:

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

Điều này làm việc với using Microsoft.VisualStudio.TestTools.UnitTesting;


Tôi thực sự ngạc nhiên khi không có cách nào ngắn gọn để khẳng định rằng một phương thức đưa ra một ngoại lệ như trong JUnit. Trừ khi có những hàm ý tôi không biết, đây có lẽ là câu trả lời phù hợp nhất hiện nay.
NetherGranite

3

Để mở rộng câu trả lời liên tục và để cung cấp thêm chức năng của NUnit, bạn có thể làm điều này:

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

Ví dụ:

// 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

Đã lâu rồi kể từ khi vấn đề này được nêu ra, tôi nhận ra, nhưng gần đây tôi đã gặp phải điều tương tự và đề xuất chức năng này cho MSTest:

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

sử dụng:

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

Xem thêm tại đây: http://phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-occured/


2

Vì tôi bị làm phiền bởi tính dài dòng của một số mẫu NUnit mới, tôi sử dụng một cái gì đó như thế này để tạo mã sạch hơn cho cá nhân tôi:

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

Việc sử dụng là:

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

1
TestDelegate là gì?
reggaeg Ức

1
Nó cho phép bạn truyền mã để thực thi như một tham số. Đó là một lớp trong khung NUnit (v3.2.0.0).
Savage
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.