2010-01-14 6 views
10

Quale pensi sia il modo più pulito di fare più affermazioni su un risultato? In passato ho messo tutti gli stessi test ma questo comincia a sentirsi un po 'sporco, ho appena giocato con un'altra idea usando setup.Best practice per più asserzioni sullo stesso risultato in C#

[TestFixture] 
public class GridControllerTests 
{ 
    protected readonly string RequestedViewId = "A1"; 

    protected GridViewModel Result { get; set;} 

    [TestFixtureSetUp] 
    public void Get_UsingStaticSettings_Assign() 
    { 
     var dataRepository = new XmlRepository("test.xml"); 

     var settingsRepository = new StaticViewSettingsRepository(); 

     var controller = new GridController(dataRepository, settingsRepository); 

     this.Result = controller.Get(RequestedViewId); 

    } 

    [Test] 
    public void Get_UsingStaticSettings_NotNull() 
    { 
     Assert.That(this.Result,Is.Not.Null); 
    } 

    [Test] 
    public void Get_UsingStaticSettings_HasData() 
    { 
     Assert.That(this.Result.Data,Is.Not.Null); 
     Assert.That(this.Result.Data.Count,Is.GreaterThan(0)); 
    } 

    [Test] 
    public void Get_UsingStaticSettings_IdMatches() 
    { 
     Assert.That(this.Result.State.ViewId,Is.EqualTo(RequestedViewId)); 
    } 

    [Test] 
    public void Get_UsingStaticSettings_FirstTimePageIsOne() 
    { 
     Assert.That(this.Result.State.CurrentPage, Is.EqualTo(1)); 
    } 
} 

risposta

3

Quello che è necessario tenere a è il modello di Arrange, Act, Assert (e quindi terminare il test). Nel tuo caso tutta la disposizione è nel TestFixtureSetUp, così come lo è l'azione in fase di test. Mi piacerebbe riorganizzarlo un po ', potrebbe diventare ingombrante quando avrai più test. Come nota la Dockers, si dovrebbero evitare pesanti configurazioni di test, che possono diventare problemi - sono "taglia unica" per tutti i test della classe e quindi possono diventare più pesanti della maggior parte dei test necessari.

Se si è tentati di proseguire con un'altra azione di follow-up e quindi più asserzioni, inserire questo in un test separato.

Non ho alcun problema con l'inserimento di più asserzioni nello stesso test, purché contribuiscano a testare la stessa cosa (cioè fanno parte della stessa "Asserzione logica"). In questo caso, qualsiasi numero di asserzioni sul contenuto di questo.Result.Data sarebbe OK da parte mia: controllerebbero tutti lo stesso valore del risultato. Il tuo Get_UsingStaticSettings_HasData lo fa molto chiaramente. È preferibile utilizzare un messaggio di errore univoco su ogni assert in modo che sia più semplice stabilire quale assert non è riuscito.

In alternativa, è possibile racchiudere i relativi asserdi in un unico metodo. questo è utile per i soliti motivi di DRY se lo usi più di una volta, ma per il resto non vedo che è una grande differenza.

In sintesi
* Effettuare un'azione per test
* Dopo l'azione, utilizzare come molti legati afferma come è necessario testare una cosa
* Terminare il test lì.

15

Avere più asserzioni nello stesso test può portare a Assertion Roulette, quindi questo è qualcosa di cui si dovrebbe sempre fare attenzione.

Tuttavia, Asserzione Roulette è per lo più un problema quando le affermazioni sono estranei. Se sono concettualmente strettamente correlate, molte asserzioni possono spesso essere viste come una singola Asserzioni logiche.

In molti casi è possibile ottenere il meglio da entrambi i mondi incapsulando esplicitamente tale asserzione logica in un tipo o metodo personalizzato.

+0

"Asserzioni logiche" è una buona parola per questo. È interessante notare che l'esempio di Assertion Roulette è essenzialmente un esempio di "come sbagliare non facendo Arrange-Act-Assert". I concetti sono diversi? – Anthony

+0

@Anthony: i concetti sono diversi, sebbene siano strettamente correlati. È molto meno probabile che tu senta il dolore della Assertion Roulette se segui AAA (o Four Phase Test come xUnit Test Patterns lo chiama), ma sarei comunque in grado di avere Asserzioni completamente indipendenti nello stesso blocco Assert. –

+3

L'idea di Assertion Roulette può essere ridotta a icona in NUnit specificando un commento nella dichiarazione Assert. Invece di fare "Assert.That (condition)" usa "Assert.That (condition, failureMessage)" dove 'failureMessage' è informazioni su cosa Assert stava verificando. Questo ti permetterà di sapere esattamente quale Assert all'interno di un unit test fallito. – Pedro

2

È possibile utilizzare Oapt - Un NUnit Addin per l'esecuzione di uno affermare per test:

[TestFixture] 
public class GridControllerTests 
{ 
    [TestCase, ForEachAssert] 
    public void Get_UsingStaticSettings_Assign() 
    { 
     var dataRepository = new XmlRepository("test.xml"); 
     var settingsRepository = new StaticViewSettingsRepository(); 
     var controller = new GridController(dataRepository, settingsRepository); 

     var result = controller.Get("A1"); 

     AssertOne.From(
     () => Assert.That(this.Result,Is.Not.Null), 
     () => Assert.That(this.Result.Data,Is.Not.Null), 
     () => Assert.That(this.Result.Data.Count,Is.GreaterThan(0)), 
     () => Assert.That(this.Result.State.ViewId,Is.EqualTo(RequestedViewId)), 
     () => Assert.That(this.Result.State.CurrentPage, Is.EqualTo(1))); 
    } 
} 

Questo creerà 5 diversi casi di test, uno per ogni assert.

0

Tendo a mettere le asserzioni da sole solo se sono di valore per conto proprio. Se voglio un'affermazione di per sé, faccio un'affermazione personalizzato:

AssertThatMyObjectMatches(field1, field2, field3, field4, myObject); 

A volte, però, mi piace avere più di un esempio in un test. Lo faccio quando metà del comportamento non ha valore senza l'altra metà.

Assert.True(list.IsEmpty()); 

list.Add(new Thing()); 
Assert.False(list.IsEmpty()); 

Altri, tra cui gran parte della comunità di Ruby, hanno un'opinione diversa su questo.E 'principalmente guidato da Dave Astels' post sul blog, qui:

http://www.artima.com/weblogs/viewpost.jsp?thread=35578

trovo il metodo ' Un'affermazione per test' molto utile per cose come la convalida, in cui ogni piccolo aspetto è prezioso. Altrimenti non mi preoccupo così tanto.

Qualunque cosa funzioni per voi e il vostro team è probabilmente la strada giusta. Tendo a fare tutto ciò che sembra semplice e facile da cambiare per essere la cosa giusta dopo, quando ho un'idea migliore di quale sia la cosa giusta. Inserisco anche molti commenti a livello di unità Given/When/Then in esempi più complessi e divido la classe se diventa troppo complessa da comprendere.

Il motivo per cui si scrivono i test in questo modo non è tale da poter cogliere cose che si interrompono. È per aiutare le persone a capire il codice e cambiarlo senza rompere le cose in primo luogo.