webentwicklung-frage-antwort-db.com.de

Am besten testen Sie Ausnahmen mit Assert, um sicherzustellen, dass sie ausgelöst werden

Denken Sie, dass dies ein guter Weg ist, um 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.

82
Hannoun Yassir

Ich habe ein paar verschiedene Muster, die ich benutze. Ich verwende das ExpectedException -Attribut die meiste Zeit, wenn eine Ausnahme erwartet wird. Dies ist in den meisten Fällen ausreichend, in einigen Fällen reicht dies jedoch nicht aus. Die Ausnahme kann möglicherweise nicht abgefangen werden - 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, z. B. dass eine Transaktion zurückgesetzt wird oder ein Wert noch festgelegt wurde. In diesen Fällen wickle ich es in ein try/catch Block, der die genaue Ausnahme erwartet, führt einen Assert.Fail Wenn der Code erfolgreich ist und auch allgemeine Ausnahmen abfängt, 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 )
        );
    }
}
127
tvanfosson

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

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

//async version
await Assert.ThrowsExceptionAsync<SomeException>(
  () => myObject.SomeMethodAsync()
);
30
Icaro Bombonato

Ich bin neu hier und habe nicht den Ruf, etwas zu kommentieren oder abzulehnen, wollte aber auf einen Fehler im Beispiel in Andy Whites Antwort hinweisen :

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

In allen mir vertrauten Unit-Testing-Frameworks löst Assert.Fail Eine Ausnahme aus, sodass der generische Fang den Fehler des Tests tatsächlich maskiert. Wenn SomethingThatCausesAnException() nicht auslöst, wird der Assert.Fail - Wert angezeigt, der jedoch niemals an den Testläufer weitergeleitet wird, um einen Fehler anzuzeigen.

Wenn Sie die erwartete Ausnahme abfangen müssen (d. H. Um bestimmte Details wie die Nachricht/Eigenschaften der Ausnahme zu bestätigen), ist es wichtig, den bestimmten erwarteten Typ und nicht die Basis-Ausnahmeklasse abzufangen. Dadurch könnte die Assert.Fail - Ausnahme ausgelöst werden (vorausgesetzt, Sie lösen nicht denselben Ausnahmetyp aus wie Ihr Unit-Testing-Framework), lassen jedoch die Überprüfung der Ausnahme zu, die von Ihrer SomethingThatCausesAnException() Methode.

17
allgeek

Ab v 2.5, NUnit gibt es die folgenden Methodenebenen Asserts zum Testen von Ausnahmen:

Assert.Throws , das nach einem genauen Ausnahmetyp sucht:

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

Und Assert.Catch, der auf eine Ausnahme eines bestimmten Typs oder einen von diesem Typ abgeleiteten Ausnahmetyp prüft:

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

Wenn Sie Unit-Tests debuggen, die Ausnahmen auslösen, sollten Sie außerdem verhindern, dass VS die Ausnahme unterbricht .

Bearbeiten

Um nur ein Beispiel für Matthews Kommentar zu geben, die Rückkehr des generischen Assert.Throws und Assert.Catch ist 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);
15
StuartLC

Leider hat MSTest STILL nur das ExpectedException-Attribut (zeigt nur, wie sehr sich MS um MSTest kümmert), welches IMO ziemlich schrecklich ist, weil es das Arrange/Act/Assert-Muster bricht und es Ihnen nicht erlaubt, genau anzugeben, welche Codezeile Sie von der Ausnahme erwarten auftreten am.

Wenn ich MSTest benutze (/ von einem Client gezwungen), benutze 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));
11
bytedev

Als Alternative zum ExpectedException Attribut definiere ich manchmal zwei hilfreiche Methoden für meine Testklassen:

AssertThrowsException() nimmt einen Delegaten und behauptet, dass er die erwartete Ausnahme mit der erwarteten Nachricht auslöst.

AssertDoesNotThrowException() nimmt denselben Delegaten und behauptet, dass er keine Ausnahme auslöst.

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

Wenn ich sie benutze, könnte mein Unit Test Code so 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);

Schön und ordentlich, oder?

Meine Methoden AssertThrowsException() und AssertDoesNotThrowException() sind auf einer gemeinsamen 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);
    }
}
9
GrahamS

Markieren Sie den Test mit dem ExpectedExceptionAttribute (dies ist der Begriff in NUnit oder MSTest; Benutzer anderer Unit-Testing-Frameworks müssen möglicherweise übersetzen).

4
itowlson

Bei den meisten .net-Unit-Testing-Frameworks können Sie der Testmethode das Attribut [ExpectedException] hinzufügen. Dies kann Ihnen jedoch nicht sagen, dass die Ausnahme an dem Punkt aufgetreten ist, an dem Sie dies erwartet haben. Hier kann xunit.net helfen.

Mit xunit haben Sie Assert.Throws, sodass Sie Folgendes tun können:

    [Fact]
    public void CantDecrementBasketLineQuantityBelowZero()
    {
        var o = new Basket();
        var p = new Product {Id = 1, NetPrice = 23.45m};
        o.AddProduct(p, 1);
        Assert.Throws<BusinessException>(() => o.SetProductQuantity(p, -3));
    }

[Fakt] ist das xunit-Äquivalent von [TestMethod]

3
Steve Willcock

Schlagen Sie die Verwendung der sauberen Delegatensyntax von NUnit vor.

Beispiel zum Testen von ArgumentNullExeption:

[Test]
[TestCase(null)]
public void FooCalculation_InvalidInput_ShouldThrowArgumentNullExeption(string text)
{
    var foo = new Foo();
    Assert.That(() => foo.Calculate(text), Throws.ArgumentNullExeption);

    //Or:
    Assert.That(() => foo.Calculate(text), Throws.Exception.TypeOf<ArgumentNullExeption>);
}
0
Shahar Shokrani