Sfortunatamente la definizione di unit test è molto sfocata. Una delle definizioni più citate è fatta attraverso le proprietà che dovrebbero avere i test, che sono
- corre veloce
- è isolato (nessuna interazione tra i test, ad esempio eseguirli in qualsiasi ordine e ottenere sempre lo stesso risultato)
- non richiede alcuna configurazione esterna
- fornisce un risultato pass / fail coerente
È tratto dal libro di Roy Osherove, l'arte del collaudo di unità.
Se dovessi aggiungere una definizione, direi che un test unitario si basa sul risultato di un percorso logico attraverso un metodo in cui hai il controllo sul risultato di tutte le dipendenze coinvolte (fw, questa è solo la mia definizione). / p>
Immagina di dover creare un metodo che controlli se una stringa è valida, e in tal caso fa qualcosa, altrimenti fa qualcosa di diverso.
Ecco come potremmo scrivere quel metodo se vogliamo che sia unit testabile:
class SomeClass
{
IStringAnalyzer stringAnalizer;
ILogger logger;
public SomeClass(IStringAnalyzer stringAnalyzer, ILogger logger)
{
this.logger = logger;
this.stringAnalyzer = stringAnalyzer;
}
public void SomeMethod(string someParameter)
{
if (stringAnalyzer.IsValid(someParameter))
{
//do something with someParameter
}else
{
logger.Log("Invalid string");
}
}
stringAnalyser e logger sono delle dipendenze perché vogliamo essere in grado di controllare ciò che restituiscono (attraverso uno stub) e come vengono utilizzati (tramite una simulazione).
Questo è così puoi scrivere un test come questo (che ha tutte le belle caratteristiche che descrive Roy nel suo libro):
[Test]
public void SomeMethod_InvalidParameter_CallsLogger
{
Rhino.Mocks.MockRepository mockRepository = new Rhino.Mocks.MockRepository();
IStringAnalyzer s = mockRepository.Stub<IStringRepository>();
s.Stub(s => s.IsValid("something, doesnt matter").IgnoreParameters().Return(false);
ILogger l = mockRepository.DynamicMock<ILogger>();
SomeClass someClass = new SomeClass(s, l);
mockRepository.ReplayAll();
someClass.SomeMethod("What you put here doesnt really matter because the stub will always return false");
l.AssertWasCalled(l => l.Log("Invalid string"));
}
I vantaggi sono che le "molte preoccupazioni" sono tutte disaccoppiate e sono molto facili da individuare quando si esegue il test per primo. Vedresti che dovresti lasciare la logica di StringAnalizer fuori dalla classe che stai testando perché la necessità di controllare se una stringa è valida o meno sarebbe evidente.
Quel test fa uso di un framework di isolamento (rhino mocks) e spiego cosa è qui: link
Per quanto riguarda la discussione di TDD produce davvero disegni migliori, tutto quello che posso dire è questo: link
E questo: link