2009-03-09 12 views
9

Vorrei testare un metodo su una classe che ho creato, ma questo metodo richiede che venga chiamato prima un altro metodo. Esempio:Metodi dipendenti da test unità

// This would work 
MyClass myClass1 = new MyClass(mockDevice); 
myClass1.Run(myDatastructure); 
myClass1.Stop(); 

// This would throw an InvalidOperationException 
MyClass myClass2 = new MyClass(mockDevice); 
myClass2.Stop(); 

Run sta iniziando un'operazione su un dispositivo hardware, e Stop è, naturalmente, cercare di fermare questa operazione (l'invio di un reset-comando e di iniziare un timeout-timer).

Comunque vorrei provare varie post-condizioni di chiamare Stop, ma vorrei non dover chiamare Run, perché sto testando Stop - non Run! Mi piacerebbe qualcosa di simile:

MyClass myClass = new MyClass(mockDevice); 
myClass.Stop(); 
Assert.IsTrue(mockDevice.ResetCalled); 

Finora vedo solo una soluzione possibile, e che è quello di creare un TestableMyClass che eredita da MyClass, che permette di impostare lo stato interno destro dell'istanza MyClass prima chiamando Stop. Il problema con questa soluzione è che devo cambiare la mia implementazione MyClass per avere membri protetti invece di membri privati, e io non come come l'idea di dover cambiare l'implementazione per testarla!

Devo usare questa soluzione, c'è un errore nel mio progetto, o c'è un modo più intelligente di farlo?

risposta

6

Per quanto vedo, si sta già testando Stop nei due modi in cui può essere utilizzato (con e senza un'operazione in esecuzione). Finché il mockDevice sta facendo il suo lavoro, mi sembra che tu lo stia verificando ragionevolmente. Idealmente dovresti essere in grado di verificare i comandi inviati al dispositivo ecc. (Che la maggior parte dei framework mock renderà semplice).

+0

Quindi pensi che sia giusto chiamare Run, Stop, Assert? – toxvaerd

+0

Sì, penso di sì. E vale anche la pena chiamare Stop senza Run per controllare che venga lanciata l'eccezione richiesta. In definitiva, non è valido chiamare Stop senza Esegui prima ... –

4

In questa situazione, personalmente, avrei due test per questo:

  1. Test senza Run() di essere chiamato prima. Vorrei testare se davvero lancia l'eccezione. Vorrei anche verificare se le condizioni del post sono quelle che mi aspetto che siano.
  2. Test eseguito con Run(). Vorrei testare solo le condizioni post, che mi aspetto.

Questi sono gli unici due importanti usi del metodo che hanno comportamenti diversi, quindi li testerei entrambi.

EDIT: Capisco, perché non si desidera chiamare l'esecuzione, prima dell'arresto - si pensa che se l'esecuzione non riesce, il test, che si suppone solo per testare il metodo di arresto molto probabilmente fallirà pure.

Tuttavia, suppongo che abbiate anche un test per il metodo run. Ciò significa che quando i test, che testano il comportamento del metodo run, i test del metodo pass-stop devono passare pure. Se i test del metodo di esecuzione falliscono, i risultati dei test del metodo di esecuzione non sono definiti, possono o non possono fallire.

Quindi, direi, non abbiate paura di chiamare altri metodi dipendenti nei test, ma assicuratevi di testare quei metodi dipendenti in test separati.

+0

Sto già testando il metodo Stop che genera un'eccezione, ma vedo il tuo punto! – toxvaerd

+1

Grazie, per la risposta, ma accetterò la risposta di Marc Gravell, come prima :-) – toxvaerd

1

Non aiuterà il problema immediato, ma tendo a preferire lo stato della mappatura al tipo piuttosto che avere oggetti con modalità differenti.

IdleDevice idle = new IdleDevice(mockDevice); 
RunningDevice running = idle.Run(myDatastructure); 
running.Stop(); 

// This would not compile, as IdleDevice has no Stop method 
IdleDevice idle = new IdleDevice(mockDevice); 
idle.Stop(); 

Se non è possibile compilare situazioni impossibili, non è necessario testarle.

+0

Mi piace quel disegno! Prenderò in considerazione l'utilizzo di questo, ma per ora testerò semplicemente Stop seguito da Run. Grazie – toxvaerd