Best way to test exceptions with Assert to ensure they will be thrown

前端 未结 9 1743
悲哀的现实
悲哀的现实 2020-12-02 06:58

Do you think that this is a good way for testing exceptions? Any suggestions?

Exception exception = null;
try{
    //I m sure that an exeption will happen he         


        
相关标签:
9条回答
  • 2020-12-02 07:54

    As an alternative to using ExpectedException attribute, I sometimes define two helpful methods for my test classes:

    AssertThrowsException() takes a delegate and asserts that it throws the expected exception with the expected message.

    AssertDoesNotThrowException() takes the same delegate and asserts that it does not throw an exception.

    This pairing can be very useful when you want to test that an exception is thrown in one case, but not the other.

    Using them my unit test code might look like this:

    ExceptionThrower callStartOp = delegate(){ testObj.StartOperation(); };
    
    // Check exception is thrown correctly...
    AssertThrowsException(callStartOp, typeof(InvalidOperationException), "StartOperation() called when not ready.");
    
    testObj.Ready = true;
    
    // Check exception is now not thrown...
    AssertDoesNotThrowException(callStartOp);
    

    Nice and neat huh?

    My AssertThrowsException() and AssertDoesNotThrowException() methods are defined on a common base class as follows:

    protected delegate void ExceptionThrower();
    
    /// <summary>
    /// Asserts that calling a method results in an exception of the stated type with the stated message.
    /// </summary>
    /// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
    /// <param name="expectedExceptionType">The expected type of the exception, e.g. typeof(FormatException).</param>
    /// <param name="expectedExceptionMessage">The expected exception message (or fragment of the whole message)</param>
    protected void AssertThrowsException(ExceptionThrower exceptionThrowingFunc, Type expectedExceptionType, string expectedExceptionMessage)
    {
        try
        {
            exceptionThrowingFunc();
            Assert.Fail("Call did not raise any exception, but one was expected.");
        }
        catch (NUnit.Framework.AssertionException)
        {
            // Ignore and rethrow NUnit exception
            throw;
        }
        catch (Exception ex)
        {
            Assert.IsInstanceOfType(expectedExceptionType, ex, "Exception raised was not the expected type.");
            Assert.IsTrue(ex.Message.Contains(expectedExceptionMessage), "Exception raised did not contain expected message. Expected=\"" + expectedExceptionMessage + "\", got \"" + ex.Message + "\"");
        }
    }
    
    /// <summary>
    /// Asserts that calling a method does not throw an exception.
    /// </summary>
    /// <remarks>
    /// This is typically only used in conjunction with <see cref="AssertThrowsException"/>. (e.g. once you have tested that an ExceptionThrower
    /// method throws an exception then your test may fix the cause of the exception and then call this to make sure it is now fixed).
    /// </remarks>
    /// <param name="exceptionThrowingFunc">Delegate that calls the method to be tested.</param>
    protected void AssertDoesNotThrowException(ExceptionThrower exceptionThrowingFunc)
    {
        try
        {
            exceptionThrowingFunc();
        }
        catch (NUnit.Framework.AssertionException)
        {
            // Ignore and rethrow any NUnit exception
            throw;
        }
        catch (Exception ex)
        {
            Assert.Fail("Call raised an unexpected exception: " + ex.Message);
        }
    }
    
    0 讨论(0)
  • 2020-12-02 07:56

    I'm new here and don't have the reputation to comment or downvote, but wanted to point out a flaw in the example in Andy White's reply:

    try
    {
        SomethingThatCausesAnException();
        Assert.Fail("Should have exceptioned above!");
    }
    catch (Exception ex)
    {
        // whatever logging code
    }
    

    In all unit testing frameworks I am familiar with, Assert.Fail works by throwing an exception, so the generic catch will actually mask the failure of the test. If SomethingThatCausesAnException() does not throw, the Assert.Fail will, but that will never bubble out to the test runner to indicate failure.

    If you need to catch the expected exception (i.e., to assert certain details, like the message / properties on the exception), it's important to catch the specific expected type, and not the base Exception class. That would allow the Assert.Fail exception to bubble out (assuming you aren't throwing the same type of exception that your unit testing framework does), but still allow validation on the exception that was thrown by your SomethingThatCausesAnException() method.

    0 讨论(0)
  • 2020-12-02 07:57

    Now, 2017, you can do it easier with the new MSTest V2 Framework:

    Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());
    
    //async version
    await Assert.ThrowsExceptionAsync<SomeException>(
      () => myObject.SomeMethodAsync()
    );
    
    0 讨论(0)
提交回复
热议问题