ฉันจะใช้ Assert ได้อย่างไรแถวเพื่อยืนยันประเภทของข้อยกเว้น


247

ฉันจะใช้Assert.Throwsเพื่อยืนยันประเภทของข้อยกเว้นและข้อความจริงได้อย่างไร

บางสิ่งเช่นนี้

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

วิธีที่ฉันกำลังทดสอบจะส่งข้อความที่มีประเภทเดียวกันหลายข้อความพร้อมกับข้อความที่แตกต่างกันและฉันต้องการวิธีทดสอบว่าข้อความที่ถูกต้องนั้นถูกส่งออกไปแล้วแต่บริบท

คำตอบ:


444

Assert.Throws ส่งกลับข้อยกเว้นที่ถูกส่งออกไปซึ่งช่วยให้คุณสามารถยืนยันข้อยกเว้นได้

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

ดังนั้นหากไม่มีข้อยกเว้นถูกโยนทิ้งหรือมีการยกเว้นประเภทที่ไม่ถูกต้องการAssert.Throwsยืนยันครั้งแรกจะล้มเหลว อย่างไรก็ตามหากมีการโยนข้อยกเว้นประเภทที่ถูกต้องคุณสามารถยืนยันข้อยกเว้นจริงที่คุณบันทึกไว้ในตัวแปรได้

โดยใช้รูปแบบนี้คุณสามารถยืนยันสิ่งอื่นนอกเหนือจากข้อความข้อยกเว้นเช่นในกรณีของArgumentExceptionและอนุพันธ์คุณสามารถยืนยันว่าชื่อพารามิเตอร์ถูกต้อง:

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

นอกจากนี้คุณยังสามารถใช้ API ได้อย่างคล่องแคล่วสำหรับการยืนยันเหล่านี้:

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

หรืออีกวิธีหนึ่ง

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

เคล็ดลับเล็กน้อยเมื่อเข้าไปยุ่งกับข้อความแสดงข้อยกเว้นคือการตกแต่งวิธีทดสอบด้วยSetCultureAttributeเพื่อให้แน่ใจว่าข้อความที่ส่งออกมานั้นใช้วัฒนธรรมที่คาดหวังไว้ สิ่งนี้เข้ามาเล่นถ้าคุณเก็บข้อความข้อยกเว้นของคุณเป็นทรัพยากรเพื่ออนุญาตให้มีการแปล


สิ่งนี้มีประโยชน์จริง ๆ สำหรับฉัน - ฉันต้องการวิธีที่จะแสดงข้อผิดพลาดฉันไม่ได้อ่านถ้ามีการส่งคืนค่าโดยวิธี Assert.Throws ขอบคุณ
Haroon

6
+1 ขอบคุณที่แสดง API ของ Fluent ด้วยเหตุผลบางอย่างที่ฉันมีปัญหาในการทำความเข้าใจวิธีใช้เพียงแค่จากเอกสาร NUnit เพียงอย่างเดียว
aolszowka

เมื่อคุณต้องการยืนยันข้อความคุณสามารถใช้คุณสมบัติข้อความได้โดยตรงแทน "คุณสมบัติ"
Marcel

25

ตอนนี้คุณสามารถใช้ExpectedExceptionคุณสมบัติเช่น

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

2
สิ่งนี้รบกวนฉันเล็กน้อยเมื่อเห็นครั้งแรกเนื่องจากการทดสอบที่ชัดเจนไม่มีการยืนยันซึ่งเป็นกลิ่นสำหรับฉัน นี่เป็นคุณสมบัติที่ดี แต่อย่างใดอย่างหนึ่งควรหารือในทีมที่ส่วนนี้ควรได้รับการใช้งานมากกว่า Assert.Throws
Marcel

14
+1 เป็นวิธีที่ดีในการทดสอบข้อยกเว้น สิ่งเดียวที่ต้องจำไว้คือเรื่องนี้คือในทางทฤษฎีบรรทัดใด ๆ ของรหัสที่โยน InvalidOperationException ด้วยข้อความนั้นจะผ่านการทดสอบรวมถึงรหัสในการทดสอบของคุณซึ่งเตรียมข้อมูลการทดสอบ / วัตถุหรือวิธีอื่นใดที่คุณอาจต้องดำเนินการก่อน สิ่งที่คุณสนใจในการทดสอบอาจทำให้เกิดผลบวกที่ผิดพลาด แน่นอนว่าขึ้นอยู่กับความเฉพาะเจาะจงของข้อความและประเภทของข้อยกเว้นที่คุณกำลังทดสอบ ด้วยAssert.Throwคุณสามารถกำหนดเป้าหมายบรรทัดที่แน่นอนที่คุณสนใจได้
Nope

21
แอตทริบิวต์ ExpectedException เลิกใช้แล้วใน 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
ด้วยการแนะนำของผู้ประกอบการ nameof ที่ฉันจะแก้ไข Anwser ที่ยอดเยี่ยมนี้ไปที่:Assert.That(myTestDelegate, Throws.ArgumentException .With.Property(nameof(ArgumentException.Message)).EqualTo("your argument is invalid."));
ซามูเอล

@Samuel การแก้ไขนั้นจะใช้การอ้างอิงที่พิมพ์ได้ดีซึ่งเป็นสิ่งที่ดี แต่ในทางกลับกันมันเป็นชื่อสถานที่ให้บริการที่มีความปั่นป่วนต่ำมาก เรื่องของรสนิยมฉันคิดว่า
Jordan Morris

1
Exception.Messageผมเห็นด้วยกับคุณเกี่ยวกับ ฉันยังคงแนะนำอย่างน้อยเพิ่มทางเลือกนี้เพราะWith.Propertyสามารถนำไปใช้กับวัตถุอื่นเช่นกันซึ่งกรณีนี้จะปรับปรุงเสถียรภาพของรหัส
ซามูเอล

5

นี่เป็นคำถามเก่าที่เกี่ยวข้องกับคำตอบที่ล้าสมัยดังนั้นฉันจึงเพิ่มโซลูชันปัจจุบัน:

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

ใช้งานได้กับ using Microsoft.VisualStudio.TestTools.UnitTesting;


ฉันประหลาดใจจริง ๆ ที่ไม่มีวิธีรัดกุมเพื่อยืนยันว่าวิธีการโยนข้อยกเว้นเช่นใน JUnit เว้นแต่จะมีความหมายที่ฉันไม่ทราบนี่อาจเป็นคำตอบที่เกี่ยวข้องที่สุดในปัจจุบัน
NetherGranite

3

หากต้องการขยายคำตอบของแบบถาวรและเพื่อมอบฟังก์ชันการทำงานของ NUnit ให้มากขึ้นคุณสามารถทำได้:

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

ตัวอย่าง:

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

เป็นเวลานานแล้วที่ปัญหานี้ได้รับการหยิบยกขึ้นมาฉันรู้ แต่เมื่อเร็ว ๆ นี้ฉันได้พบกับสิ่งเดียวกันและแนะนำฟังก์ชั่นนี้สำหรับ MSTest:

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

การใช้งาน:

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

เพิ่มเติมได้ที่นี่: http://phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-occured/


2

เนื่องจากฉันถูกรบกวนจากความฟุ่มเฟื่อยของรูปแบบใหม่ของ NUnit ฉันจึงใช้สิ่งนี้เพื่อสร้างรหัสที่สะอาดสำหรับฉันเป็นการส่วนตัว:

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

การใช้งานคือ:

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

1
TestDelegate คืออะไร
reggaeguitar

1
อนุญาตให้คุณส่งรหัสเพื่อเรียกใช้งานเป็นพารามิเตอร์ มันเป็นคลาสในกรอบ NUnit (v3.2.0.0)
Savage
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.