2008-11-02 2 views
5

Ich bin komplett neu bei C# und NUnit.Ist NUnits ExpectedExceptionAttribute nur eine Möglichkeit zu testen, ob etwas eine Ausnahme auslöst?

In Boost.Test gibt es eine Familie von BOOST_*_THROW Makros. In Pythons Testmodul gibt es TestCase.assertRaises Methode.

Soweit ich es verstehe, in C# mit NUnit (2.4.8) ist die einzige Methode, Ausnahme-Test zu tun, ExpectedExceptionAttribute zu verwenden.

Warum sollte ich ExpectedExceptionAttribute über - sagen wir - Boost.Test Ansatz bevorzugen? Welche Argumentation kann hinter dieser Designentscheidung stehen? Warum ist das bei C# und NUnit besser?

Schließlich, wenn ich beschließe, ExpectedExceptionAttribute zu verwenden, wie kann ich einige zusätzliche Tests durchführen, nachdem die Ausnahme ausgelöst und abgefangen wurde? Nehmen wir an, ich möchte die Anforderung testen, dass das Objekt gültig sein muss, nachdem ein Setzer System.IndexOutOfRangeException ausgelöst hat. Wie würden Sie den folgenden Code reparieren und wie erwartet arbeiten?

[Test] 
public void TestSetterException() 
{ 
    Sth.SomeClass obj = new SomeClass(); 

    // Following statement won't compile. 
    Assert.Raises("System.IndexOutOfRangeException", 
        obj.SetValueAt(-1, "foo")); 

    Assert.IsTrue(obj.IsValid()); 
} 

Edit: Vielen Dank für Ihre Antworten. Heute habe ich eine Es sind die Testsblog entry, wo alle drei von Ihnen beschriebenen Methoden erwähnt werden (und eine kleinere Variante). Es ist schade, dass ich es nicht vor :-(finden konnte

Antwort

13

ich bin überrascht, dass ich nicht dieses Muster noch erwähnt gesehen haben. David Arno ist sehr ähnlich, aber ich ziehe die Einfachheit dieses:

try 
{ 
    obj.SetValueAt(-1, "foo"); 
    Assert.Fail("Expected exception"); 
} 
catch (IndexOutOfRangeException) 
{ 
    // Expected 
} 
Assert.IsTrue(obj.IsValid()); 
+0

Will nicht Assert.Fail ("...") dump Sie aus dem Test mit einer AssertionFailedException? –

+1

Ja. Wenn der Aufruf von SetValueAt jedoch eine Ausnahme auslöst (was wir erreichen möchten), wird die Steuerung sofort in den catch-Block verschoben und umgeht Assert.Fail, wodurch der Test bestanden wird. Wenn SetValueAt keine Ausnahme auslöst, ist das Verhalten nicht so, und der Test sollte fehlschlagen. – yfeldblum

+0

Das ist besser als meine Art, es zu tun. Ich werde deinen Ansatz in Zukunft verwenden :) –

10

Wenn Sie NUnit 2.5 gibt es einige nette helpers es verwenden können

Assert.That(delegate { ... }, Throws.Exception<ArgumentException>()) 
+1

+1. Ich bevorzuge diesen Stil, da es weniger Zeremonien benötigt, um den Test zu schreiben, besonders wenn Sie eine Referenz zum Überprüfen von Nachrichten usw. behalten müssen. Sie können auch var exception = Assert.Throws (() => myInstance.DoSomethingInvalid ()); –

2

Ich habe immer den folgenden Ansatz:..

bool success = true; 
try { 
    obj.SetValueAt(-1, "foo"); 
} catch() { 
    success = false; 
} 

assert.IsFalse(success); 

... 
+0

Persönlich würde ich eine bestimmte Ausnahme abfangen, aber das wird funktionieren; -p –

+0

@Marc, gültiger Punkt: Es sollte die spezifische Ausnahme im Test abfangen und andere durchlassen, so dass sie als Fehler statt Testfehler angezeigt werden. –

4

Die MbUnit Syntax ist

Assert.Throws<IndexOutOfRangeException>(delegate { 
    int[] nums = new int[] { 0, 1, 2 }; 
    nums[3] = 3; 
}); 
2

Ihre bevorzugte Syntax:

Assert.Raises("System.IndexOutOfRangeException", 
       obj.SetValueAt(-1, "foo")); 

woiuldn't Arbeit mit C# sowieso - die obj.SetValueAt bewertet werden würde, und das Ergebnis an Assert.Raises übergeben. Wenn SetValue eine Ausnahme auslöst, würden Sie nie Assert.Raises bekommen.

Sie könnten eine Hilfsmethode schreiben, es zu tun:

void Raises<T>(Action action) where T:Exception { 
    try { 
     action(); 
     throw new ExpectedException(typeof(T)); 
    catch (Exception ex) { 
     if (ex.GetType() != typeof(T)) { 
     throw; 
     } 
    } 
} 

der die ähnliche Syntax erlaubt:

Assert.Raises<System.IndexOutOfRangeException>(() => 
    obj.SetValueAt(-1, "foo") 
; 
Verwandte Themen