c# example Der beste Weg, Ausnahmen mit Assert zu testen, um sicherzustellen, dass sie ausgelöst werden




c# textbox validation example (7)

Leider hat MSTest STILL nur das ExpectedException-Attribut (zeigt nur an, wieviel MS sich für MSTest interessiert), was IMO ziemlich scheußlich ist, weil es das Arrange / Act / Assert-Muster unterbricht und es nicht erlaubt, genau festzulegen, welche Codezeile die Ausnahme erwartet auftreten auf.

Wenn ich MSTest benutze (/ von einem Client erzwungen) verwende ich immer diese Hilfsklasse:

public static class AssertException
{
    public static void Throws<TException>(Action action) where TException : Exception
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
            return;
        }
        Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
    }

    public static void Throws<TException>(Action action, string expectedMessage) where TException : Exception
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.GetType() == typeof(TException), "Expected exception of type " + typeof(TException) + " but type of " + ex.GetType() + " was thrown instead.");
            Assert.AreEqual(expectedMessage, ex.Message, "Expected exception with a message of '" + expectedMessage + "' but exception with message of '" + ex.Message + "' was thrown instead.");
            return;
        }
        Assert.Fail("Expected exception of type " + typeof(TException) + " but no exception was thrown.");
    }
}

Anwendungsbeispiel:

AssertException.Throws<ArgumentNullException>(() => classUnderTest.GetCustomer(null));

Denken Sie, dass dies eine gute Möglichkeit ist, Ausnahmen zu testen? Irgendwelche Vorschläge?

Exception exception = null;
try{
    //I m sure that an exeption will happen here
}
catch (Exception ex){
    exception = ex;
}

Assert.IsNotNull(exception);

Ich benutze MS Test.


Answer #1

Markieren Sie den Test mit dem ExpectedExceptionAttribute (dies ist der Begriff in NUnit oder MSTest; Benutzer anderer Einheitentestframeworks müssen möglicherweise übersetzt werden).


Answer #2

Ich habe ein paar verschiedene Muster, die ich benutze. Ich verwende das ExpectedException Attribut die meiste Zeit, wenn eine Ausnahme erwartet wird. Dies reicht in den meisten Fällen aus, es gibt jedoch Fälle, in denen dies nicht ausreicht. Die Ausnahme ist möglicherweise nicht abfragbar - da sie von einer Methode ausgelöst wird, die durch Reflektion aufgerufen wird - oder ich möchte nur überprüfen, ob andere Bedingungen erfüllt sind. Beispielsweise wird eine Transaktion zurückgesetzt oder ein Wert wurde noch festgelegt. In diesen Fällen Assert.Fail ich es in einen try/catch Block, der die genaue Ausnahme erwartet, führt eine Assert.Fail wenn der Code erfolgreich ist, und fängt auch generische Ausnahmen ab, um sicherzustellen, dass keine andere Ausnahme ausgelöst wird.

Erster Fall:

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void MethodTest()
{
     var obj = new ClassRequiringNonNullParameter( null );
}

Zweiter Fall:

[TestMethod]
public void MethodTest()
{
    try
    {
        var obj = new ClassRequiringNonNullParameter( null );
        Assert.Fail("An exception should have been thrown");
    }
    catch (ArgumentNullException ae)
    {
        Assert.AreEqual( "Parameter cannot be null or empty.", ae.Message );
    }
    catch (Exception e)
    {
        Assert.Fail(
             string.Format( "Unexpected exception of type {0} caught: {1}",
                            e.GetType(), e.Message )
        );
    }
}

Answer #3

Ab Assert 2.5 verfügt NUnit über die folgende Methode Level Assert s zum Testen von Ausnahmen:

Assert.Throws , die auf einen genauen Ausnahmetyp prüfen:

Assert.Throws<NullReferenceException>(() => someNullObject.ToString());

Und Assert.Catch , das auf eine Ausnahme eines bestimmten Typs oder einen von diesem Typ abgeleiteten Ausnahmetyp Assert.Catch :

Assert.Catch<Exception>(() => someNullObject.ToString());

Nebenbei: Wenn Sie Unit-Tests testen, bei denen Exceptions ausgelöst werden, möchten Sie möglicherweise verhindern, dass VS bei der Exception bricht .

Bearbeiten

Um nur ein Beispiel für den Kommentar von Matthew zu geben, ist die Rückgabe der generischen Assert.Throws und Assert.Catch die Ausnahme mit dem Typ der Ausnahme, die Sie dann zur weiteren Überprüfung untersuchen können:

// The type of ex is that of the generic type parameter (SqlException)
var ex = Assert.Throws<SqlException>(() => MethodWhichDeadlocks());
Assert.AreEqual(1205, ex.Number);

Answer #4

Als Alternative zur Verwendung des ExpectedException Attributs habe ich manchmal zwei hilfreiche Methoden für meine ExpectedException definiert:

AssertThrowsException() nimmt einen Delegaten und behauptet, dass es die erwartete Ausnahme mit der erwarteten Nachricht AssertThrowsException() .

AssertDoesNotThrowException() nimmt denselben Delegaten und gibt an, dass keine Ausnahme AssertDoesNotThrowException() .

Diese Paarung kann sehr nützlich sein, wenn Sie testen möchten, ob eine Ausnahme in einem Fall ausgelöst wird, aber nicht in der anderen.

Mit ihnen könnte mein Unit Test Code wie folgt aussehen:

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

Nett und ordentlich huh?

Meine AssertThrowsException() und AssertDoesNotThrowException() sind für eine gemeinsame Basisklasse wie folgt definiert:

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

Answer #5

Jetzt, 2017, können Sie es mit dem neuen MSTest V2 Framework einfacher machen:

Assert.ThrowsException<Exception>(() => myClass.MyMethodWithError());

Answer #6

Ich bin neu hier und habe nicht den Ruf zu kommentieren oder runterzuschauen, sondern wollte in dem Beispiel in Andy Whites Antwort auf einen Fehler hinweisen:

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

In allen Unit-Test-Frameworks, die ich kenne, funktioniert Assert.Fail indem es eine Ausnahme Assert.Fail , so dass der generische Catch den Fehler des Tests maskiert. Wenn SomethingThatCausesAnException() nicht Assert.Fail wird das Assert.Fail , aber das wird nie an den Test-Runner sprudeln, um einen Fehler anzuzeigen.

Wenn Sie die erwartete Ausnahme abfangen müssen (dh bestimmte Details wie die Nachricht / Eigenschaften der Ausnahme bestätigen), ist es wichtig, den bestimmten erwarteten Typ und nicht die Basis-Exception-Klasse abzufangen. Das würde ermöglichen, dass die Assert.Fail Ausnahme Assert.Fail wird (vorausgesetzt, Sie werfen nicht den gleichen Typ von Ausnahme wie das Unit- Assert.Fail ), aber die Validierung der Ausnahme, die von Ihrer SomethingThatCausesAnException() -Methode ausgelöst wurde, erlaubt.





unit-testing