ฉันจะใช้ Assert เพื่อตรวจสอบว่ามีข้อผิดพลาดเกิดขึ้นได้อย่างไร


830

ฉันจะใช้Assert(หรือคลาสทดสอบอื่น ๆ ได้อย่างไร) เพื่อตรวจสอบว่ามีข้อผิดพลาดเกิดขึ้นหรือไม่


คุณใช้กรอบการทดสอบหน่วยใด
เควิน Pullin

3
Visual Studio Integrated
Alex

4
แอตทริบิวต์ ExpectedException ไม่ช่วยหรือไม่ ref: msdn.microsoft.com/en-us/library/…
shahkalpesh

2
ตลกผมเพิ่งเสร็จสิ้นการมองหาคำตอบนี้พบได้ที่stackoverflow.com/questions/741029/testing-exceptions
dfjacobs

ดูเพิ่มเติมได้ที่: stackoverflow.com/questions/741029/…
bytedev

คำตอบ:


978

สำหรับ "Visual Studio Team Test" จะปรากฏว่าคุณใช้แอตทริบิวต์ ExpectedException กับวิธีการทดสอบ

ตัวอย่างจากเอกสารประกอบที่นี่: บททดสอบหน่วยกับการทดสอบทีม Visual Studio

[TestMethod]
[ExpectedException(typeof(ArgumentException),
    "A userId of null was inappropriately allowed.")]
public void NullUserIdInConstructor()
{
   LogonInfo logonInfo = new LogonInfo(null, "P@ss0word");
}

25
แอตทริบิวต์ ExpectedException ด้านบนทำงานใน NUnit เช่นกัน (แต่ [TestMethod] ควรเป็น [Test])
dbkk

5
@dbkk: อยากทำงานตรงเดียวกันใน NUnit - ข้อความจะถือว่าเป็นสตริงที่ต้อง matcvh ข้อความแสดงข้อยกเว้น (และ IU คิดว่าทำให้รู้สึกมากขึ้น)
Ruben Bartelink

29
คุณลักษณะนี้ทำให้งานเสร็จและเป็นคุณสมบัติในตัวสำหรับโปรแกรมเมอร์ c # แต่ฉันไม่แนะนำให้ใช้เพราะมันไม่ยืดหยุ่นพอ พิจารณาว่าจะเกิดอะไรขึ้นหากประเภทการยกเว้นถูกส่งออกมาจากรหัสการตั้งค่าการทดสอบของคุณ: การทดสอบผ่าน แต่ไม่ได้ทำในสิ่งที่คุณคาดหวัง หรือถ้าคุณต้องการทดสอบสถานะของวัตถุข้อยกเว้น ฉันมักจะต้องการใช้ StringAssert.Contains (e.Message ... ) มากกว่าทดสอบข้อความทั้งหมด ใช้วิธีการยืนยันตามที่อธิบายไว้ในคำตอบอื่น ๆ
สตีฟ

3
หลีกเลี่ยงการใช้ ExpectedException ใน NUnit เนื่องจากจะลดลงใน NUnit 3.0 ฉันต้องการใช้ Assert.Throws <SpecificException> ()
Terence

5
คุณสามารถใช้ Assert.ThrowsException <T> และ Assert.ThrowsExceptionAsync <T> ภายใน MsTest
Gopal Krishnan

257

โดยปกติกรอบการทดสอบของคุณจะมีคำตอบสำหรับสิ่งนี้ แต่ถ้ามันไม่ยืดหยุ่นเพียงพอคุณสามารถทำสิ่งนี้ได้เสมอ:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // If it gets to this line, no exception was thrown
} catch (GoodException) { }

@Jonas ชี้ว่าสิ่งนี้ไม่ได้ผลสำหรับการยกเว้นฐาน:

try {
    somethingThatShouldThrowAnException();
    Assert.Fail(); // raises AssertionException
} catch (Exception) {
    // Catches the assertion exception, and the test passes
}

หากคุณต้องได้รับการยกเว้นคุณต้องทำการยืนยัน Assert.Fail () อีกครั้ง แต่นี่เป็นสัญญาณที่คุณไม่ควรเขียนด้วยมือ ตรวจสอบกรอบการทดสอบของคุณเพื่อดูตัวเลือกหรือดูว่าคุณสามารถใช้ข้อยกเว้นที่มีความหมายเพื่อทดสอบได้หรือไม่

catch (AssertionException) { throw; }

คุณควรปรับวิธีการนี้ให้เข้ากับสิ่งที่คุณต้องการรวมถึงการระบุว่ามีข้อยกเว้นประเภทใดบ้าง หากคุณคาดหวังเพียงบางประเภทเท่านั้นให้catchปิดบล็อกโดยใช้:

} catch (GoodException) {
} catch (Exception) {
    // not the right kind of exception
    Assert.Fail();
}

20
+1 ฉันใช้วิธีนี้แทนแอททริบิวต์เมื่อฉันต้องการยืนยันมากกว่าประเภทข้อยกเว้น ตัวอย่างเช่นถ้าต้องการตรวจสอบว่าฟิลด์บางอย่างในอินสแตนซ์ข้อยกเว้นถูกตั้งค่าเป็นค่าที่แน่นอน
Pavel Repin

2
คุณไม่จำเป็นต้องระบุข้อความแสดงข้อผิดพลาด นี่เพียงพอแล้ว: [ExpectedException (typeof (ArgumentException))]
mibollma

5
ฉันคิดว่าวิธีนี้ดีที่สุด [ExpectedException (typeof (ArgumentException))] มีการใช้งานหากการทดสอบนั้นง่าย แต่ในมุมมองของฉันเป็นวิธีแก้ปัญหาที่ขี้เกียจและการใช้งานได้สบายอาจนำไปสู่ข้อผิดพลาด วิธีการแก้ปัญหานี้ช่วยให้คุณสามารถควบคุมการทดสอบที่ถูกต้องมากขึ้นรวมถึงการทดสอบ Writeline ไปยังรายงานการทดสอบที่คุณใช้งาน
evilfish

12
ระวังให้ดีเพราะ Assert.Fail () ยกข้อยกเว้นถ้าคุณจับมันผ่านการทดสอบ!
Jonas

4
@ Vinnyq12 สิ่งที่ฉันหมายถึงคือการทดสอบครั้งแรกในตัวอย่างข้างต้นจะไม่ล้มเหลว การทดสอบล้มเหลวหากมีการโยนข้อยกเว้น (และไม่ใช่ "การจับ" โดย ExpectedExceptionAttribute)
Jonas

113

วิธีที่ฉันชอบสำหรับการใช้งานนี้คือการเขียนวิธีที่เรียกว่า Throws และใช้วิธีเดียวกับ Assert วิธีอื่น ๆ น่าเสียดายที่. NET ไม่อนุญาตให้คุณเขียนวิธีการขยายแบบคงที่ดังนั้นคุณจึงไม่สามารถใช้วิธีนี้ราวกับว่ามันเป็นของ build ในคลาส Assert แค่สร้างอีกอันที่เรียกว่า MyAssert หรืออะไรที่คล้ายกัน ชั้นเรียนมีลักษณะดังนี้:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        public static void Throws<T>( Action func ) where T : Exception
        {
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }

            if ( !exceptionThrown )
            {
                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but not thrown", typeof(T))
                    );
            }
        }
    }
}

นั่นหมายความว่าการทดสอบหน่วยของคุณเป็นดังนี้:

[TestMethod()]
public void ExceptionTest()
{
    String testStr = null;
    MyAssert.Throws<NullReferenceException>(() => testStr.ToUpper());
}

รูปลักษณ์และพฤติกรรมใดที่เหมือนส่วนที่เหลือของไวยากรณ์ทดสอบหน่วยของคุณ


1
กำจัดการตั้งค่าสถานะบูลและส่งการโยนบนบรรทัดโดยตรงหลังจากการเรียกใช้สำหรับการใช้งานที่กระชับยิ่งขึ้น
gt

11
สิ่งเดียวที่ทำให้สิ่งนี้ดีกว่าคือการให้ฟังก์ชันส่งคืนข้อยกเว้นที่ตรวจพบเพื่อให้คุณสามารถยืนยันได้ว่าสิ่งต่างๆเช่นแอตทริบิวต์ในข้อยกเว้นนั้นถูกต้อง
Mark Hildreth

2
ขอบคุณ! ดูเหมือนว่าวิธีที่ดีที่สุดสำหรับฉันเพราะมันเป็นวิธีสั้น ๆ ในการทดสอบข้อยกเว้นหลายวิธีในวิธีเดียว นอกจากนี้ยังอ่านได้มากขึ้นอีกด้วย
David Sherret

2
@MickeyPerlstein คุณสมบัติทำลายกฎ AAA สำหรับการทดสอบ โดยเฉพาะอย่างยิ่งหากการจัดการของคุณเกิดขึ้นเพื่อโยนข้อยกเว้นก่อนที่คุณจะได้รับพระราชบัญญัติการทดสอบของคุณผ่าน ... eek!
freedomn-m

2
ในที่สุดไมโครซอฟท์ก็พร้อมที่จะอัพเดต MSTest - v2 รองรับAssert.ThrowsException<T>และAssert.ThrowsExceptionAsync<T>- ดูblogs.msdn.microsoft.com/visualstudioalm/2017/02/25/…
Quango

62

ถ้าคุณใช้ NUNIT คุณสามารถทำสิ่งนี้:

Assert.Throws<ExpectedException>(() => methodToTest());


นอกจากนี้ยังเป็นไปได้ที่จะเก็บข้อยกเว้นที่ส่งออกไปเพื่อตรวจสอบความถูกต้องเพิ่มเติม:

ExpectedException ex = Assert.Throws<ExpectedException>(() => methodToTest());
Assert.AreEqual( "Expected message text.", ex.Message );
Assert.AreEqual( 5, ex.SomeNumber);

ดู: http://nunit.org/docs/2.5/exceptionAsserts.html


60

หากคุณกำลังใช้ MSTest ซึ่งเดิมไม่มีExpectedExceptionแอตทริบิวต์คุณสามารถทำสิ่งนี้ได้:

try 
{
    SomeExceptionThrowingMethod()
    Assert.Fail("no exception thrown");
}
catch (Exception ex)
{
    Assert.IsTrue(ex is SpecificExceptionType);
}

3
ใช้งานได้ แต่ฉันไม่แนะนำโดยทั่วไปเนื่องจากตรรกะมีความซับซ้อนมากเกินไป ไม่ได้บอกว่ามันซับซ้อน แต่ให้พิจารณาว่าคุณเขียนบล็อกของรหัสนี้สำหรับการทดสอบหลาย ๆ - 10s, 100s ของการทดสอบ ตรรกะนี้ต้องได้รับการฝึกฝนให้เป็นวิธีการยืนยันที่ออกแบบมาอย่างดี ดูคำตอบอื่น ๆ
สตีฟ

35

ระวังการใช้ ExpectedException เนื่องจากอาจนำไปสู่ข้อผิดพลาดหลายประการดังที่แสดงไว้ที่นี่:

http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx

และที่นี่:

http://xunit.github.io/docs/comparisons.html

หากคุณต้องการทดสอบข้อยกเว้นมีวิธีการขมวดคิ้วน้อยกว่า คุณสามารถใช้วิธีลอง {act / fail} catch {assert} ซึ่งสามารถเป็นประโยชน์สำหรับเฟรมเวิร์กที่ไม่ได้รับการสนับสนุนโดยตรงสำหรับการทดสอบข้อยกเว้นอื่น ๆ นอกเหนือจาก ExpectedException

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

คุณสามารถค้นหา xUnit.NET ได้ที่ github: http://xunit.github.io/


4
โปรดทราบว่า NUnit 2.5 ยังรองรับ Assert.Throws ด้วยสไตล์ของไวยากรณ์ในขณะนี้เช่นกัน - nunit.com/index.php?p=releaseNotes&r=2.5
Alconja

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

@Ant: MS คัดลอก NUnit ... ดังนั้นคำถามจริงคือทำไม NUnit คิดว่ามันเป็นความคิดที่ดี?
jrista

28

MSTest (v2) ตอนนี้มีฟังก์ชั่น Assert.ThrowsException ซึ่งสามารถใช้ดังนี้:

Assert.ThrowsException<System.FormatException>(() =>
            {
                Story actual = PersonalSite.Services.Content.ExtractHeader(String.Empty);
            }); 

คุณสามารถติดตั้งได้ด้วย nuget: Install-Package MSTest.TestFramework


ในปีพ. ศ. 2561 สิ่งนี้ถือว่าเป็นแนวปฏิบัติที่ดีที่สุดเนื่องจากจะทำการตรวจสอบเฉพาะหน่วยที่อยู่ภายใต้การทดสอบเท่านั้นที่จะทำการโยนไม่ใช่รหัสอื่น
CM

24

ในโครงการฉันกำลังทำงานกับเรามีวิธีแก้ไขปัญหาอื่นที่ทำเช่นนี้

ก่อนอื่นฉันไม่ชอบ ExpectedExceptionAttribute เพราะมันต้องคำนึงถึงว่าการเรียกเมธอดใดที่ทำให้เกิดข้อยกเว้น

ฉันทำสิ่งนี้ด้วยวิธีช่วยเหลือแทน

ทดสอบ

[TestMethod]
public void AccountRepository_ThrowsExceptionIfFileisCorrupt()
{
     var file = File.Create("Accounts.bin");
     file.WriteByte(1);
     file.Close();

     IAccountRepository repo = new FileAccountRepository();
     TestHelpers.AssertThrows<SerializationException>(()=>repo.GetAll());            
}

HelperMethod

public static TException AssertThrows<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (TException ex)
        {
            return ex;
        }
        Assert.Fail("Expected exception was not thrown");

        return null;
    }

เรียบร้อยไม่ได้;)


14

มันเป็นคุณสมบัติของวิธีการทดสอบ ... คุณไม่ได้ใช้ Assert ดูเหมือนว่านี้:

[ExpectedException(typeof(ExceptionType))]
public void YourMethod_should_throw_exception()

13

คุณสามารถดาวน์โหลดแพ็คเกจจาก Nuget ได้โดยใช้: PM> Install-Package MSTestExtensionsที่เพิ่มAssert.Throws ()ไวยากรณ์ในรูปแบบของ nUnit / xUnit ไปยัง MsTest

คำแนะนำระดับสูง: ดาวน์โหลดแอสเซมบลีและสืบทอดจากBaseTestและคุณสามารถใช้ไวยากรณ์Assert.Throws ()

วิธีการหลักสำหรับการใช้งาน Throws มีลักษณะดังนี้:

public static void Throws<T>(Action task, string expectedMessage, ExceptionMessageCompareOptions options) where T : Exception
{
    try
    {
        task();
    }
    catch (Exception ex)
    {
        AssertExceptionType<T>(ex);
        AssertExceptionMessage(ex, expectedMessage, options);
        return;
    }

    if (typeof(T).Equals(new Exception().GetType()))
    {
        Assert.Fail("Expected exception but no exception was thrown.");
    }
    else
    {
        Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.", typeof(T)));
    }
}

การเปิดเผย: ฉันรวบรวมแพคเกจนี้

ข้อมูลเพิ่มเติม: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html


ขอบคุณสำหรับตัวอย่าง คุณมีตัวอย่างวิธีทดสอบ Assert.DoesNotThrow () หรือเทียบเท่าหรือไม่?
Lane Goolsby

10

คุณสามารถทำสิ่งนี้ได้ง่ายๆด้วยบรรทัดเดียว

หากการดำเนินการของคุณfoo.bar()เป็นแบบซิงค์:

await Assert.ThrowsExceptionAsync<Exception>(() => foo.bar());

หากfoo.bar()ไม่ใช่แบบอะซิงก์

Assert.ThrowsException<Exception>(() => foo.bar());

1
มีคำตอบอื่น ๆ อีกมากมายสำหรับฉันฉันกำลังมองหาวิธีจดชวเลขเพื่อทดสอบเงื่อนไขความล้มเหลวที่ทราบโดยประเภทข้อยกเว้นเท่านั้นทำให้เป็นกรณีทดสอบที่อ่านง่ายที่สุด หมายเหตุ: ประเภทข้อยกเว้นไม่ตรงกับคลาสยกเว้นที่สืบทอดมาเช่น try-catch มาตรฐานดังนั้นตัวอย่างข้างต้นจะไม่ดักจับArgumentExceptionอินสแตนซ์ ลองจับแบบเก่าและทดสอบการตอบสนองข้อยกเว้นยังคงเป็นที่ต้องการหากคุณมีเกณฑ์ขั้นสูงในการทดสอบ แต่สำหรับหลาย ๆ กรณีของฉันสิ่งนี้ช่วยได้มาก!
Chris Schaller

5

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

public static class ExceptionAssert
{
    private static T GetException<T>(Action action, string message="") where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            return exception;
        }
        throw new AssertFailedException("Expected exception " + typeof(T).FullName + ", but none was propagated.  " + message);
    }

    public static void Propagates<T>(Action action) where T : Exception
    {
        Propagates<T>(action, "");
    }

    public static void Propagates<T>(Action action, string message) where T : Exception
    {
        GetException<T>(action, message);
    }

    public static void Propagates<T>(Action action, Action<T> validation) where T : Exception
    {
        Propagates(action, validation, "");
    }

    public static void Propagates<T>(Action action, Action<T> validation, string message) where T : Exception
    {
        validation(GetException<T>(action, message));
    }
}

ตัวอย่างการใช้:

    [TestMethod]
    public void Run_PropagatesWin32Exception_ForInvalidExeFile()
    {
        (test setup that might propagate Win32Exception)
        ExceptionAssert.Propagates<Win32Exception>(
            () => CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location, new string[0]));
        (more asserts or something)
    }

    [TestMethod]
    public void Run_PropagatesFileNotFoundException_ForExecutableNotFound()
    {
        (test setup that might propagate FileNotFoundException)
        ExceptionAssert.Propagates<FileNotFoundException>(
            () => CommandExecutionUtil.Run("NotThere.exe", new string[0]),
            e => StringAssert.Contains(e.Message, "NotThere.exe"));
        (more asserts or something)
    }

หมายเหตุ

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

แตกต่างจากคนอื่น ๆ ฉันใช้ 'เผยแพร่' แทน 'พ่น' เนื่องจากเราสามารถทดสอบได้ว่าข้อยกเว้นแพร่กระจายจากการโทรหรือไม่ เราไม่สามารถทดสอบได้โดยตรงว่ามีข้อผิดพลาดเกิดขึ้น แต่ฉันคิดว่าคุณสามารถโยนภาพเพื่อหมายถึง: โยนและไม่จับ

ความคิดสุดท้าย

ก่อนที่จะเปลี่ยนไปใช้วิธีการแบบนี้ฉันจะพิจารณาใช้แอตทริบิวต์ ExpectedException เมื่อการทดสอบตรวจสอบประเภทข้อยกเว้นเท่านั้นและการใช้บล็อกลอง / catch หากต้องการการตรวจสอบเพิ่มเติม แต่ไม่เพียงแค่ฉันต้องคิดว่าเทคนิคใดที่จะใช้สำหรับการทดสอบแต่ละครั้ง แต่การเปลี่ยนรหัสจากเทคนิคหนึ่งไปอีกเทคนิคหนึ่งเนื่องจากความต้องการที่เปลี่ยนไปนั้นไม่ใช่ความพยายามเล็กน้อย การใช้วิธีการที่สอดคล้องกันอย่างใดอย่างหนึ่งจะช่วยประหยัดความพยายามทางจิต

ดังนั้นโดยสรุปวิธีการนี้จึงเป็นกีฬา: ใช้งานง่ายยืดหยุ่นและทนทาน (ยากที่จะทำผิด)


4

ผู้ช่วยเหลือที่ได้รับจาก @Richiban ใช้งานได้ดียกเว้นว่าจะไม่จัดการกับสถานการณ์ที่มีข้อผิดพลาดเกิดขึ้น แต่ไม่ใช่ประเภทที่คาดไว้ ที่อยู่ต่อไปนี้ที่:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace YourProject.Tests
{
    public static class MyAssert
    {
        /// <summary>
        /// Helper for Asserting that a function throws an exception of a particular type.
        /// </summary>
        public static void Throws<T>( Action func ) where T : Exception
        {
            Exception exceptionOther = null;
            var exceptionThrown = false;
            try
            {
                func.Invoke();
            }
            catch ( T )
            {
                exceptionThrown = true;
            }
            catch (Exception e) {
                exceptionOther = e;
            }

            if ( !exceptionThrown )
            {
                if (exceptionOther != null) {
                    throw new AssertFailedException(
                        String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.", typeof(T), exceptionOther.GetType()),
                        exceptionOther
                        );
                }

                throw new AssertFailedException(
                    String.Format("An exception of type {0} was expected, but no exception was thrown.", typeof(T))
                    );
            }
        }
    }
}

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

@ มาร์ตินฉันจะลบรหัสที่เกี่ยวข้องกับข้อยกเว้นอื่น ๆ และเพียงแค่ rethrow จากประโยคที่สองจับ
Tom Lint

4

เมื่อคุณพูดถึงการใช้การเรียนการทดสอบอื่น ๆ เป็นตัวเลือกที่ดีกว่าExpectedExceptionแอตทริบิวต์คือการใช้Shoudly 's Should.Throw

Should.Throw<DivideByZeroException>(() => { MyDivideMethod(1, 0); });

สมมติว่าเรามีความต้องการที่ลูกค้าจะต้องมีอยู่ในการสร้างการสั่งซื้อ ถ้าไม่วิธีการควรจะส่งผลในการCreateOrderForCustomer ArgumentExceptionจากนั้นเราสามารถเขียน:

[TestMethod]
public void NullUserIdInConstructor()
{
  var customer = new Customer(name := "Justin", address := null};

  Should.Throw<ArgumentException>(() => {
    var order = CreateOrderForCustomer(customer) });
}

สิ่งนี้ดีกว่าการใช้ExpectedExceptionแอตทริบิวต์เนื่องจากเรามีความเฉพาะเจาะจงเกี่ยวกับสิ่งที่ควรทิ้งข้อผิดพลาด ทำให้ข้อกำหนดในการทดสอบของเราชัดเจนขึ้นและทำให้การวินิจฉัยง่ายขึ้นเมื่อการทดสอบล้มเหลว

หมายเหตุนอกจากนี้ยังมีShould.ThrowAsyncการทดสอบวิธีการแบบอะซิงโครนัส


4

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

var testDelegate = () => MyService.Method(params);
Assert.Throws<Exception>(testDelegate);

4

ในการทดสอบหน่วย VS ในตัวถ้าคุณเพียงต้องการตรวจสอบว่า "ข้อยกเว้นใด ๆ " ถูกโยน แต่คุณไม่ทราบประเภทคุณสามารถใช้ catch all:

[TestMethod]
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
public void ThrowExceptionTest()
{
    //...
}

3

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

/// <summary>
/// Checks to make sure that the input delegate throws a exception of type TException.
/// </summary>
/// <typeparam name="TException">The type of exception expected.</typeparam>
/// <param name="methodToExecute">The method to execute to generate the exception.</param>
public static void AssertRaises<TException>(Action methodToExecute) where TException : System.Exception
{
    try
    {
        methodToExecute();
    }
    catch (TException) {
        return;
    }  
    catch (System.Exception ex)
    {
        Assert.Fail("Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
    }
    Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");  
}


2

สิ่งนี้จะขึ้นอยู่กับกรอบการทดสอบที่คุณใช้

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

[ExpectedException(typeof(ArgumentException))]

2

ในกรณีที่ใช้NUnitให้ลองสิ่งนี้:

Assert.That(() =>
        {
            Your_Method_To_Test();
        }, Throws.TypeOf<Your_Specific_Exception>().With.Message.EqualTo("Your_Specific_Message"));

2

มีห้องสมุดที่ยอดเยี่ยมที่เรียกว่าNFluentซึ่งเพิ่มความเร็วและลดความยุ่งยากในการเขียนคำยืนยันของคุณ

มันค่อนข้างตรงไปตรงมาที่จะเขียนคำยืนยันในการโยนข้อยกเว้น:

    [Test]
    public void given_when_then()
    {
        Check.ThatCode(() => MethodToTest())
            .Throws<Exception>()
            .WithMessage("Process has been failed");
    }

1

แม้ว่านี่จะเป็นคำถามเก่า แต่ฉันต้องการเพิ่มความคิดใหม่ให้กับการอภิปราย ฉันได้ขยาย Arrange, Act, Assert pattern เพื่อคาดหวัง, Arrange, Act, Assert คุณสามารถสร้างตัวชี้ข้อยกเว้นที่คาดไว้จากนั้นยืนยันว่าได้รับมอบหมายให้ สิ่งนี้ให้ความรู้สึกที่สะอาดกว่าการทำ Asserts ของคุณใน catch catch โดยปล่อยให้ Act ของคุณส่วนใหญ่ใช้สำหรับโค้ดบรรทัดเดียวเพื่อเรียกเมธอดภายใต้การทดสอบ คุณไม่จำเป็นต้องไปAssert.Fail();หรือreturnมาจากหลาย ๆ จุดในรหัส ข้อยกเว้นอื่น ๆ ที่ส่งออกไปจะทำให้การทดสอบล้มเหลวเนื่องจากจะไม่ถูกตรวจจับและหากมีการยกเว้นประเภทที่คุณคาดไว้ แต่ไม่ใช่สิ่งที่คุณคาดหวังการยืนยันข้อความหรือคุณสมบัติอื่น ๆ ของ ข้อยกเว้นช่วยให้แน่ใจว่าการทดสอบของคุณจะไม่ผ่านโดยไม่ตั้งใจ

[TestMethod]
public void Bar_InvalidDependency_ThrowsInvalidOperationException()
{
    // Expectations
    InvalidOperationException expectedException = null;
    string expectedExceptionMessage = "Bar did something invalid.";

    // Arrange
    IDependency dependency = DependencyMocks.Create();
    Foo foo = new Foo(dependency);

    // Act
    try
    {
        foo.Bar();
    }
    catch (InvalidOperationException ex)
    {
        expectedException = ex;
    }

    // Assert
    Assert.IsNotNull(expectedException);
    Assert.AreEqual(expectedExceptionMessage, expectedException.Message);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.