c# - मैं यह सत्यापित करने के लिए आर्टर्ट का उपयोग कैसे करूं कि एक अपवाद फेंक दिया गया है?




unit-testing mstest (14)

"विजुअल स्टूडियो टीम टेस्ट" के लिए ऐसा प्रतीत होता है कि आप परीक्षण की विधि में अपेक्षित एक्सेप्शन विशेषता लागू करते हैं।

यहां प्रलेखन से नमूना: विजुअल स्टूडियो टीम टेस्ट के साथ एक यूनिट परीक्षण वॉकथ्रू

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

यह सत्यापित करने के लिए कि एक अपवाद फेंक दिया गया है, मैं Assert (या अन्य टेस्ट क्लास?) का उपयोग कैसे करूं?


ExpectException का उपयोग करने से सावधान रहें, क्योंकि यह यहां दिखाए गए कई नुकसान का कारण बन सकता है:

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} विधि का उपयोग कर सकते हैं, जो ढांचे के लिए उपयोगी हो सकता है जिनके पास ExpectException के अलावा अपवाद परीक्षणों के लिए प्रत्यक्ष समर्थन नहीं है।

XUnit.NET का उपयोग करने का एक बेहतर विकल्प है, जो एक बहुत आधुनिक, आगे दिखने वाला और एक्स्टेंसिबल यूनिट परीक्षण ढांचा है जो अन्य सभी गलतियों से सीखता है, और बेहतर होता है। ऐसा एक सुधार Assert.Throws है, जो अपवादों को जोर देने के लिए एक बेहतर वाक्यविन्यास प्रदान करता है।

आप github पर xUnit.NET पा सकते हैं: http://xunit.github.io/


आम तौर पर आपके परीक्षण ढांचे के लिए इसका जवाब होगा। लेकिन अगर यह पर्याप्त लचीला नहीं है, तो आप हमेशा ऐसा कर सकते हैं:

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

जैसा कि @ जोनास बताते हैं, यह आधार अपवाद को पकड़ने के लिए काम नहीं करता है:

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

इसे लागू करने के लिए मेरी पसंदीदा विधि थ्रो नामक एक विधि लिखना है, और इसे किसी भी अन्य एपर्ट विधि की तरह उपयोग करें। दुर्भाग्यवश, .NET आपको एक स्थिर विस्तार विधि लिखने की अनुमति नहीं देता है, इसलिए आप इस विधि का उपयोग नहीं कर सकते हैं जैसे कि यह वास्तव में आर्टर्ट क्लास में निर्माण से संबंधित है; बस एक और 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());
}

जो आपके यूनिट टेस्ट सिंटैक्स के बाकी हिस्सों की तरह दिखता है और व्यवहार करता है।


ऊपर @ रिचिबैन द्वारा प्रदान किया गया सहायक महान काम करता है सिवाय इसके कि यह उस स्थिति को संभाल नहीं लेता है जहां एक अपवाद फेंक दिया जाता है, लेकिन अपेक्षित प्रकार नहीं। निम्नलिखित पते हैं कि:

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

एक परियोजना में मैं काम कर रहा हूं हमारे पास ऐसा करने का एक और समाधान है।

सबसे पहले मुझे अपेक्षित एक्सेप्शन एट्रिब्यूट पसंद नहीं है क्योंकि यह ध्यान में रखता है कि कौन सी विधि कॉल अपवाद का कारण बनती है।

मैं इसके बजाय एक helpermethod के साथ ऐसा करते हैं।

परीक्षा

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

साफ, यह नहीं है;)


एमएसटीएस्ट में अब एक Assert.ThrowsException फ़ंक्शन है जिसका उपयोग इस तरह किया जा सकता है:

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

खैर मैं बहुत अधिक समझाऊंगा कि हर किसी ने यहां क्या कहा था ... वैसे भी, यह कोड मैंने अच्छे उत्तरों के अनुसार बनाया है :) सभी को कॉपी करने और उपयोग करने के लिए छोड़ दिया गया है ...

/// <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.");  
}

मैं ExpectedException विशेषता का उपयोग करने की अनुशंसा नहीं करता (क्योंकि यह बहुत बाधा और त्रुटि-प्रवण है) या प्रत्येक परीक्षण में एक प्रयास / पकड़ ब्लॉक लिखने के लिए (क्योंकि यह बहुत जटिल और त्रुटि-प्रवण है)। एक अच्छी तरह से डिज़ाइन किए गए जोर विधि का उपयोग करें - या तो अपने परीक्षण ढांचे द्वारा प्रदान किया गया है या अपना खुद का लिखें। यहां मैंने जो लिखा और उपयोग किया है।

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 विशेषता का उपयोग करने पर विचार किया जब एक परीक्षण ने अपवाद प्रकार को सत्यापित किया और अधिक सत्यापन की आवश्यकता होने पर प्रयास / पकड़ ब्लॉक का उपयोग कर। लेकिन, मुझे केवल यह नहीं सोचना होगा कि प्रत्येक परीक्षा के लिए किस तकनीक का उपयोग करना है, लेकिन एक तकनीक से कोड को बदलने की ज़रूरतों को बदलना जरूरी प्रयास नहीं था। एक सतत दृष्टिकोण का उपयोग मानसिक प्रयास बचाता है।

तो संक्षेप में, यह दृष्टिकोण खेल: आसानी से उपयोग, लचीलापन और मजबूती (इसे गलत करने के लिए मुश्किल)।


यदि आप एमएसटीएस्ट का उपयोग कर रहे हैं, जो मूल रूप से ExpectedException विशेषता नहीं है, तो आप यह कर सकते हैं:

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

यह इस बात पर निर्भर करेगा कि आप किस टेस्ट फ्रेमवर्क का उपयोग कर रहे हैं?

एमबीयूनीट में, उदाहरण के लिए, आप यह सुनिश्चित करने के लिए एक विशेषता के साथ अपेक्षित अपवाद निर्दिष्ट कर सकते हैं कि आपको वह अपवाद मिल रहा है जिसे आप वास्तव में उम्मीद करते हैं।

[ExpectedException(typeof(ArgumentException))]

यह परीक्षण विधि पर एक विशेषता है ... आप Assert का उपयोग नहीं करते हैं। इस तरह दिखता है:

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

हालांकि यह एक पुराना सवाल है, मैं चर्चा में एक नया विचार जोड़ना चाहता हूं। मैंने अपेक्षित, व्यवस्थित, अधिनियम, दावा करने के लिए व्यवस्था, अधिनियम, सम्मिलन पैटर्न को बढ़ाया है। आप एक अपेक्षित अपवाद सूचक बना सकते हैं, फिर जोर दें कि इसे असाइन किया गया था। यह कैच ब्लॉक में आपके आवेषण करने से क्लीनर महसूस करता है, परीक्षण के तहत विधि को कॉल करने के लिए कोड के एक पंक्ति के लिए अपने एक्ट सेक्शन को छोड़कर। आपको भी Assert.Fail(); देना नहीं है। 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);
}

एनयूनीट का उपयोग करने के मामले में, इसे आजमाएं:

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




vs-unit-testing-framework