2009-10-22 7 views
168

Come utilizzare Assert.Throws per confermare il tipo di eccezione e il testo effettivo del messaggio.Come utilizzare Assert.Throws per affermare il tipo di eccezione?

Qualcosa di simile a questo:

Assert.Throws<Exception>(
    ()=>user.MakeUserActive()).WithMessage("Actual exception message") 

Il metodo che sto testando lancia più messaggi dello stesso tipo, con diversi messaggi, e ho bisogno di un modo per testare che il messaggio corretto è gettato a seconda del contesto.

risposta

295

Assert.Throws restituisce l'eccezione generata che consente di affermare l'eccezione.

var ex = Assert.Throws<Exception>(() => user.MakeUserActive()); 
Assert.That(ex.Message, Is.EqualTo("Actual exception message")); 

Quindi, se non viene generata un'eccezione, o viene generata un'eccezione di tipo errato, il primo Assert.Throws asserzione fallirà. Tuttavia, se viene lanciata un'eccezione del tipo corretto, puoi ora far valere l'eccezione effettiva che hai salvato nella variabile.

Utilizzando questo modello è possibile affermare su altre cose oltre al messaggio di eccezione, ad es. nel caso di ArgumentException e derivati, si può affermare che il nome del parametro è corretto:

var ex = Assert.Throws<ArgumentNullException>(() => foo.Bar(null)); 
Assert.That(ex.ParamName, Is.EqualTo("bar")); 

È inoltre possibile utilizzare l'API fluente per fare questi afferma:

Assert.That(() => foo.Bar(null), 
Throws.Exception 
    .TypeOf<ArgumentNullException>() 
    .With.Property("ParamName") 
    .EqualTo("bar")); 

Un piccolo suggerimento quando valere su i messaggi di eccezione sono per decorare il metodo di prova con lo SetCultureAttribute per assicurarsi che il messaggio generato utilizzi la cultura prevista. Questo entra in gioco se si memorizzano i messaggi di eccezione come risorse per consentire la localizzazione.

+0

questo è stato davvero utile per me - volevo un modo per visualizzare l'errore, non ho nemmeno letto se un valore è stato restituito dal metodo Assert.Throws. Grazie – Haroon

+4

+1 Grazie per aver mostrato l'API Fluent, per qualche motivo ho avuto problemi a capire come usarlo solo dai documenti NUnit. – aolszowka

21

Ora è possibile utilizzare gli attributi ExpectedException, ad es.

[Test] 
[ExpectedException(typeof(InvalidOperationException), 
ExpectedMessage="You can't do that!"] 
public void MethodA_WithNull_ThrowsInvalidOperationException() 
{ 
    MethodA(null); 
} 
+0

Questo mi ha disturbato un po 'quando l'ho visto per la prima volta, perché il test apparentemente non aveva alcuna asserzione, che era un odore per me. Questa è una caratteristica piacevole, ma si dovrebbe discutere nella squadra wheter questo attribut deve abituarsi negli Assert.Throws – Marcel

+12

+1 anche un bel modo per testare le eccezioni. L'unica cosa da tenere a mente su questo è che teoricamente ogni riga di codice gettare un InvalidOperationException con quel messaggio passerà il test, incluso il codice nel test che prepara i dati di test/oggetti o qualsiasi altro metodo che potrebbe essere necessario eseguire prima della uno che sei interessato a testare, con la possibilità di ottenere un falso positivo. Ovviamente, dipende da quanto specifico è il messaggio e dal tipo di eccezione che si sta verificando. . Con 'Assert.Throw' è possibile destinare la linea esatta siete interessati a – Nope

+7

attributo ExpectedException è deprecato in NUnit 3: https://github.com/nunit/docs/wiki/Breaking-Changes –

2

E 'da molto tempo che la questione è stata sollevata, mi rendo conto, ma di recente ho incontrato la stessa cosa, e suggerire questa funzione per MSTest:

public bool AssertThrows(Action action) where T : Exception 
{ 
try {action();} 
catch(Exception exception) 
{ 
    if (exception.GetType() == typeof(T)) return true; 
} 
return false; 
} 

utilizzo:

Assert.IsTrue(AssertThrows<FormatException>(delegate{ newMyMethod(MyParameter); })); 

Altro qui: http://phejndorf.wordpress.com/2011/02/21/assert-that-a-particular-exception-has-occured/

3

Per espandere la risposta persistente e fornire più di la funzionalità di NUnit, si può fare questo:

public bool AssertThrows<TException>(
    Action action, 
    Func<TException, bool> exceptionCondition = null) 
    where TException : Exception 
{ 
    try 
    { 
     action(); 
    } 
    catch (TException ex) 
    { 
     if (exceptionCondition != null) 
     { 
      return exceptionCondition(ex); 
     } 

     return true; 
    } 
    catch 
    { 
     return false; 
    } 

    return false; 
} 

Esempi:

// No exception thrown - test fails. 
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
     () => {})); 

// Wrong exception thrown - test fails. 
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
     () => { throw new ApplicationException(); })); 

// Correct exception thrown - test passes. 
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
     () => { throw new InvalidOperationException(); })); 

// Correct exception thrown, but wrong message - test fails. 
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
     () => { throw new InvalidOperationException("ABCD"); }, 
     ex => ex.Message == "1234")); 

// Correct exception thrown, with correct message - test passes. 
Assert.IsTrue(
    AssertThrows<InvalidOperationException>(
     () => { throw new InvalidOperationException("1234"); }, 
     ex => ex.Message == "1234")); 
7
Assert.That(myTestDelegate, Throws.ArgumentException 
    .With.Property("Message").EqualTo("your argument is invalid.")); 
0

Dal momento che sto disturbato dalla verbosità di alcuni dei nuovi modelli di NUnit, io uso qualcosa di simile per creare codice che è più pulito per me personalmente:

public void AssertBusinessRuleException(TestDelegate code, string expectedMessage) 
{ 
    var ex = Assert.Throws<BusinessRuleException>(code); 
    Assert.AreEqual(ex.Message, expectedMessage); 
} 

public void AssertException<T>(TestDelegate code, string expectedMessage) where T:Exception 
{ 
    var ex = Assert.Throws<T>(code); 
    Assert.AreEqual(ex.Message, expectedMessage); 
} 

L'utilizzo è quindi:

AssertBusinessRuleException(() => service.Create(content), "Name already exists"); 
Problemi correlati