Azione separata da asserzione nei test unitari

3

Configurazione

Molti anni fa ho preso uno stile di test unitario che mi è piaciuto molto. In breve, utilizza una classe base per separare Arrangement, Action e Assertion del test in chiamate di metodo separate. A tale scopo, definisci le chiamate al metodo in [Setup] / [TestInitialize] che verranno chiamate prima di ogni esecuzione di test.

[Setup]
public void Setup()
{
   before_each(); //arrangement
   because(); //action
}

Questa classe base di solito include anche la chiamata [TearDown] quando si utilizza questa configurazione per i test di integrazione.

[TearDown]
public void Cleanup()
{
   after_each();
}

Questo spesso si rompe in una struttura in cui le classi di test ereditano da una serie di classi Given che mettono insieme il setup (cioè GivenFoo: GivenBar: WhenDoingBazz) con le Assertion che sono una riga test con un nome descrittivo di ciò che stanno coprendo

[Test]
public void ThenBuzzSouldBeTrue()
{
   Assert.IsTrue(result.Buzz);
}

Il problema

Ci sono pochissimi test che racchiudono una singola azione in modo da finire con un sacco di classi così recentemente ho preso la decisione di definire l'azione in una serie di metodi all'interno della classe di test stessa:

[Test]
public void ThenBuzzSouldBeTrue()
{
   because_an_action_was_taken();
   Assert.IsTrue(result.Buzz);
}

private void because_an_action_was_taken()
{
   //perform action here
}

Ciò comporta diversi metodi di "azione" all'interno della classe di test ma consente il raggruppamento di test simili (ad es. class == WhenTestingDifferentWaysToSetBuzz)

La domanda

Qualcun altro ha un modo migliore per separare le tre "A" dei test? La leggibilità dei test è importante per me quindi preferirei che, quando un test fallisce, che la struttura stessa dei test comunichi ciò che ha fallito. Se qualcuno può leggere la struttura di ereditarietà dei test e avere una buona idea del perché il test potrebbe fallire, credo che aggiunga molto valore ai test (cioè GivenClient: GivenUser: WhenModifyingUserPermissions: ThenReadAccessShouldBeTrue).

Sono a conoscenza del Test di accettazione, ma questo è più su un livello di Unità (o serie di unità) con strati limite imbrogliati.

EDIT : la mia domanda chiede se esiste un evento o un altro metodo per eseguire un blocco di codice prima dei singoli test (qualcosa che potrebbe essere applicato a specifici set di test senza che venga applicato a tutti i test all'interno di una classe come [Setup] lo fanno attualmente. Escludendo l'esistenza di questo evento, di cui sono abbastanza certo non esiste, esiste un altro metodo per realizzare la stessa cosa?

L'uso di [Setup] per ogni caso presenta un problema in ogni modo. Qualcosa come [Azione ("Categoria")] (un metodo di installazione che si applica a test specifici all'interno della classe) sarebbe bello ma non riesco a trovare alcun modo per farlo.

    
posta DigitalMoss 21.05.2014 - 21:12
fonte

1 risposta

0

Quello che stai facendo assomiglia molto a BDD - Sviluppo basato sul comportamento .

Forse dovresti dare un'occhiata a SpecFlow .

Ci sono anche altre opzioni per fare BDD su C #, se preferisci l'approccio basato sul codice, c'è NSpec , NBehave , StoryQ e molti altri .

Modifica: Per espandermi un po ', mi sembra che tu abbia raggiunto i limiti del framework e del linguaggio di programmazione scelti qui e non c'è più niente che puoi fare per la sintassi saggia che permetterebbe questo tipo di separazione dei ruoli di classe.

La tecnica e lo stile dei test di denominazione suggeriscono che si sta già utilizzando lo stile di test Behavior Driven , quindi il mio suggerimento è quello invece di cercare di combattere il framework di testing scelto e di convogliare inutilmente il codice di test, solo cambia il framework di test in qualcosa che è più adatto ai test che stai scrivendo.

    
risposta data 29.05.2014 - 10:28
fonte

Leggi altre domande sui tag