ฉันจะตรวจสอบ“ ไม่มีข้อยกเว้นเกิดขึ้น” ในการทดสอบหน่วย MSTest ได้อย่างไร


89

ฉันกำลังเขียนการทดสอบหน่วยสำหรับวิธีการนี้ซึ่งส่งคืน "โมฆะ" ฉันต้องการมีกรณีหนึ่งที่การทดสอบจะผ่านเมื่อไม่มีการโยนข้อยกเว้น ฉันจะเขียนใน C # ได้อย่างไร

Assert.IsTrue(????)

(ฉันเดาว่านี่คือวิธีที่ฉันควรตรวจสอบ แต่สิ่งที่เป็น "???")

ฉันหวังว่าคำถามของฉันจะชัดเจนพอ


คุณใช้ MSTest หรือ NUnit?
Matt Grande

2
ใน MSTest ข้อยกเว้นที่ไม่ถูกจับจะทำให้การทดสอบล้มเหลวโดยอัตโนมัติ คุณกำลังพยายามพิจารณาข้อยกเว้นที่ถูกจับหรือไม่?
ฟิลิป

คุณสามารถค้นหา "try-catch for C #" และจะแนะนำวิธีจัดการกับข้อยกเว้นที่โยนหรือไม่โยน
Foggzie

1
ถ้า NUnit ให้ดู Assert นั่น (แลมบ์ดา) โยนไม่มีอะไร (แม้ว่าฉันคิดว่าเพิ่งเปลี่ยนไป)
Matt Grande

คำตอบ:


139

การทดสอบหน่วยของคุณจะล้มเหลวอยู่ดีหากมีข้อยกเว้น - คุณไม่จำเป็นต้องใส่คำยืนยันพิเศษ

นี่เป็นหนึ่งในไม่กี่สถานการณ์ที่คุณจะเห็นการทดสอบหน่วยโดยไม่มีการยืนยันใด ๆ - การทดสอบจะล้มเหลวโดยปริยายหากมีการเพิ่มข้อยกเว้น

อย่างไรก็ตามหากคุณไม่ต้องการเขียนคำยืนยันสำหรับสิ่งนี้จริงๆ - อาจสามารถจับข้อยกเว้นและรายงานว่า "คาดว่าจะไม่มีข้อยกเว้น แต่ได้รับสิ่งนี้ ... " คุณสามารถทำได้:

[Test]
public void TestNoExceptionIsThrownByMethodUnderTest()
{
    var myObject = new MyObject();

    try
    {
        myObject.MethodUnderTest();
    }
    catch (Exception ex)
    {
        Assert.Fail("Expected no exception, but got: " + ex.Message);
    }
}

(ด้านบนเป็นตัวอย่างสำหรับ NUnit แต่สิ่งเดียวกันนี้ถือเป็นจริงสำหรับ MSTest)


เห็นได้ชัดว่าคุณไม่ควรไปจับข้อยกเว้นดังกล่าวเพื่อให้เป็นจริง
Servy

7
การทดสอบจะล้มเหลวก็ต่อเมื่อมีการโยนข้อยกเว้นที่ไม่ถูกจับ ขึ้นอยู่กับรหัสภายในตัวจัดการข้อยกเว้นการทดสอบหน่วยอาจผ่าน
รหัสที่กินได้

1
เป็นประโยชน์สำหรับ Ms Unittest ดังนั้นจึงไม่มีวิธี Assert.DoesNotThrow (() ใน Unittest
Başar Kaya

26

ใน NUnit คุณสามารถใช้:

Assert.DoesNotThrow(<expression>); 

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

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


1
DoesNotThrow ที่ชัดเจนเป็นสิ่งที่ดี หากคุณคุ้นเคยกับการเห็น Assert. * ในการทดสอบคุณอาจคิดว่าอีกฝ่ายขี้เกียจและลืมไป
Matt Beckman

มีสิ่งใดเทียบเท่ากับ vstest หรือ mstest?
Dan Csharpster

1
@DanCsharpster ฉันไม่คิดว่าจะมีอย่างน้อยก็ใน MSTest - เมื่อฉันต้องการฟังก์ชันนี้ใน MSTest ในอดีตฉันเคยทำสิ่งนี้: public class TestBase { //believe me, I don't like this anymore than you do. protected void AssertDoesNotThrow(Action action, string message) { try { action(); } catch (Exception) { Assert.Fail(message); } } }
Clarkeye

@Clarkeye นั่นเป็นความคิดที่น่าสนใจ ขอบคุณ! หวังว่าพวกเขาจะเรียนรู้ที่จะคัดลอก NUnit ให้ดีขึ้นในเวอร์ชันต่อ ๆ ไป ฉันยังคิดเกี่ยวกับการเขียนอะแดปเตอร์ระหว่าง vstest และ NUnit
Dan Csharpster

@DanCsharpster สิ่งหนึ่งที่คุณอาจต้องการที่จะมีลักษณะที่เป็นยืนยันได้อย่างคล่องแคล่วซึ่งได้รับการสนับสนุนที่ดีสำหรับ ShouldThrow และ ShouldNotThrow: github.com/dennisdoomen/fluentassertions/wiki#exceptions เอกสารบอกว่าเข้ากันได้กับ MSTest (แม้ว่าฉันจะใช้กับ XUnit และ NUnit เท่านั้น) อาจไม่ได้ทำทุกอย่างที่คุณต้องการ แต่คุณสามารถผสมผสานกับการยืนยัน MSTest ได้
Clarkeye

12

ไม่ได้ทดสอบว่าสิ่งที่ไม่ได้เกิดขึ้น มันเหมือนความเชื่อมั่นกับรหัสที่ไม่ทำลาย นั่นเป็นนัยว่าเราทุกคนพยายามอย่างมากที่จะไม่ทำลายโค้ดที่ไม่มีข้อผิดพลาด คุณต้องการเขียนแบบทดสอบสำหรับสิ่งนั้นหรือไม่? ทำไมต้องใช้วิธีเดียว คุณไม่ต้องการให้วิธีการทั้งหมดของคุณถูกทดสอบว่าไม่มีข้อยกเว้นหรือไม่? ตามถนนสายนั้นคุณจะได้รับการทดสอบพิเศษที่น่าเบื่อและไม่กล้าแสดงออกสำหรับทุกวิธีในฐานรหัสของคุณ มันไม่มีค่า

แน่นอนถ้าความต้องการของคุณคือการตรวจสอบวิธีการทำข้อยกเว้นจับคุณทำแบบทดสอบ (หรือการย้อนกลับของมันนิด; ทดสอบว่ามันไม่ได้โยนสิ่งที่มันควรจะจับ)

อย่างไรก็ตามแนวทาง / แนวปฏิบัติทั่วไปยังคงอยู่ - คุณไม่ได้เขียนการทดสอบสำหรับข้อกำหนดเทียม / คลุมเครือบางอย่างที่อยู่นอกขอบเขตของโค้ดที่ทดสอบ (และการทดสอบว่า "ใช้งานได้" หรือ "ไม่โยน" มักจะเป็นตัวอย่างของ เช่น - โดยเฉพาะอย่างยิ่งในสถานการณ์ที่ทราบกันดีถึงความรับผิดชอบของวิธีการ)

เพื่อให้ง่าย - เน้นที่โค้ดของคุณต้องทำและทดสอบสิ่งนั้น


10
-1 ฉันสามารถนึกถึงฟังก์ชันการทำงานเชิงบวกที่ต้องใช้และไม่ควรโยนข้อยกเว้น สำหรับวิธีการเดียวที่มีหน้าที่จัดการข้อยกเว้นให้บันทึกและดำเนินการ - โดยไม่ทิ้งข้อยกเว้นเพิ่มเติม คุณสร้างประเด็นทั่วไปที่ดี - แต่แล้วพูดอย่างสมบูรณ์ราวกับว่ามันเป็นความจริงเสมอ
Rob Levine

3
@RobLevine: ฉันเข้าใจตัวอย่างของคุณและรู้ว่าคุณเขียนแบบทดสอบในกรณีเช่นนี้ แต่อย่างที่คุณสังเกตเห็นประเด็นของฉันเกี่ยวกับการปฏิบัติทั่วไปมากกว่า - เพื่อที่จะพูดคือการทดสอบว่าโค้ดของคุณควรจะทำอะไรเทียบกับการทดสอบว่าโค้ดของคุณไม่ทำอะไร ฉันได้เขียนโพสต์ของฉันใหม่เล็กน้อยเพื่อให้ประเด็นของฉันชัดเจนและใกล้เคียงกับสิ่งที่ฉันคิดมากขึ้น ยังให้โอกาสคุณในการพิจารณาการลงคะแนนของคุณอีกครั้ง ขอบคุณสำหรับคำชี้แจงและขออภัยสำหรับการตอบกลับที่ล่าช้า
กม.

4
ลบการโหวตลง - ฉันจะไม่ทำให้มีความสุขกับการโหวตดาวน์ครั้งต่อไป!
Rob Levine

4
ในโครงการของเราเรามีคลาส htmlvalidator ซึ่งจะแสดงข้อยกเว้นหาก html ไม่ถูกต้อง ตัวอย่างเช่นเมื่อผู้ใช้ป้อนข้อมูล (โดยใช้คอนโซล) จาวาสคริปต์ในคำสั่งผสมที่หลากหลาย ดังนั้นในรหัสกรณีของฉันสิ่งที่รหัสของฉันทำคือการไม่ทิ้งข้อยกเว้น (แนวทางรายการสีขาว) และฉันต้องทดสอบสิ่งนั้น
Machet

1
ไม่เห็นด้วยกับคำตอบนี้ การทดสอบว่าไม่มีบางสิ่งในบางครั้งภายใต้สถานการณ์บางอย่างอาจเป็นการทดสอบที่ถูกต้อง
bytedev

7

คลาสผู้ช่วยนี้ทำให้ฉันคันด้วย MSTest บางทีมันอาจทำให้คุณเป็นรอยได้

[TestMethod]
public void ScheduleItsIneligibilityJob_HasValid_CronSchedule()
{
    // Arrange
    var factory = new StdSchedulerFactory();
    IScheduler scheduler = factory.GetScheduler();

    // Assert
    AssertEx.NoExceptionThrown<FormatException>(() =>
        // Act
        _service.ScheduleJob(scheduler)
    );
}

public sealed class AssertEx
{
    public static void NoExceptionThrown<T>(Action a) where T:Exception
    {
        try
        {
            a();
        }
        catch (T)
        {
            Assert.Fail("Expected no {0} to be thrown", typeof(T).Name);
        }
    }
}

@Remco Beurskens - การเพิ่มการจับทั่วไป {} ที่ส่วนท้ายของ NoExceptionThrown <T> จะระงับข้อผิดพลาดอื่น ๆ ซึ่งไม่ได้เป็นผลจากวิธีการ นี่ไม่ใช่วิธีวัตถุประสงค์ทั่วไปในการระงับข้อยกเว้นทั้งหมด มีวัตถุประสงค์เพื่อล้มเหลวเฉพาะเมื่อมีการโยนข้อยกเว้นของประเภท IS ที่รู้จัก
JJS

1
ตอนนี้เก่ามากแล้ว แต่Assertมีตัวเข้าถึงคุณสมบัติแบบซิงเกิลตันThatซึ่งสามารถใช้เป็นตะขอสำหรับวิธีการขยายได้ มันอาจจะ neater และค้นพบได้มากขึ้นที่จะมีมากกว่าAssert.That.DoesNotThrow() AssertEx.DoesNotThrow()นี่เป็นเพียงความเห็น
Richard Hauer

3

ฉันชอบดูไฟล์ Assert.Whateverตอนท้ายของการทดสอบแต่ละครั้งเพื่อความสม่ำเสมอ ... ถ้าไม่มีฉันจะแน่ใจได้หรือไม่ว่าไม่ควรจะมีอยู่ในนั้น

สำหรับฉันมันง่ายพอ ๆ กับการใส่ Assert.IsTrue(true);

ฉันรู้ว่าฉันไม่ได้ใส่รหัสนั้นลงไปโดยไม่ได้ตั้งใจและด้วยเหตุนี้ฉันจึงควรมั่นใจพอที่จะอ่านอย่างรวดเร็วว่านี่เป็นไปตามที่ตั้งใจไว้

    [TestMethod]
    public void ProjectRejectsGappedVersioningByDefault() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        Assert.Throws<ScriptProject.InvalidProjectFormatException>(() => {
            var sut = new ScriptProject(files);
        });

    }

    [TestMethod]
    public void ProjectAcceptsGappedVersionsExplicitly() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        var sut = new ScriptProject(files, true);

        Assert.IsTrue(true);   // Assert.Pass() would be nicer... build it in if you like

    }

มันไม่เหมือนกัน หากรหัสของคุณพ่นจะไม่มีการยืนยันใด ๆ และการทดสอบของคุณจะล้มเหลว คุณต้องการเชื่อมโยงเข้ากับกรอบการทดสอบโดยการยืนยันเงื่อนไข
DvS

1

เพื่อนของฉันทิมบอกฉันเกี่ยวกับExpectedException ฉันชอบ b / c นี้มากมันสั้นกว่าโค้ดน้อยลงและชัดเจนมากที่คุณกำลังทดสอบเพื่อหาข้อยกเว้น

[TestMethod()]
[ExpectedException(typeof(System.Exception))]
public void DivideTest()
{
    int numerator = 4;
    int denominator = 0;
    int actual = numerator / denominator;
}

คุณสามารถอ่านวิธีเพิ่มเติมได้ที่นี่: ExpectedException แอตทริบิวต์การใช้งาน


2
OP กำลังขอให้ไม่มีข้อยกเว้น
Daniel

ฉันจะทิ้งคำตอบไว้ที่นี่ ฉันพบคำถามนี้ในขณะที่ค้นหา Google เพื่อหาวิธีทดสอบข้อยกเว้นและคำตอบนี้ฉันคิดว่าต้องอยู่ที่นี่ OP ได้ตอบคำถามเมื่อ 7 ปีก่อน แม้แต่ลิงก์ไปยังคำตอบอื่น ๆ ที่ฉันคิดว่ามีประโยชน์
Jess

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