unit-testing
Prova doppio
Ricerca…
Osservazioni
Durante il test, a volte è utile utilizzare un test double per manipolare o verificare il comportamento del sistema sottoposto a test. I doppi vengono passati o iniettati nella classe o nel metodo sotto test invece delle istanze del codice di produzione.
Usare uno stub per fornire risposte in scatola
Uno stub è un doppio test leggero che fornisce risposte predefinite quando vengono chiamati i metodi. Quando una classe sottoposta a test si basa su un'interfaccia o una classe base, è possibile implementare una classe "stub" alternativa per il test conforme all'interfaccia.
Quindi, assumendo la seguente interfaccia,
public interface IRecordProvider {
IEnumerable<Record> GetRecords();
}
Se il seguente metodo doveva essere testato
public bool ProcessRecord(IRecordProvider provider)
Una classe stub che implementa l'interfaccia può essere scritta per restituire i dati noti al metodo in fase di test.
public class RecordProviderStub : IRecordProvider
{
public IEnumerable<Record> GetRecords()
{
return new List<Record> {
new Record { Id = 1, Flag=false, Value="First" },
new Record { Id = 2, Flag=true, Value="Second" },
new Record { Id = 3, Flag=false, Value="Third" }
};
}
}
Questa implementazione di stub può quindi essere fornita al sistema in prova, per influenzarne il comportamento.
var stub = new RecordProviderStub();
var processed = sut.ProcessRecord(stub);
Usando una struttura beffarda come uno stub
I termini Mock and Stub possono spesso diventare confusi. Parte della ragione di ciò è che molti framework di simulazione forniscono anche il supporto per la creazione di Stub senza la fase di verifica associata a Mocking.
Piuttosto che scrivere una nuova classe per implementare uno stub come nell'esempio "Uso di uno stub per fornire risposte predefinite", è possibile utilizzare invece i framework di simulazione.
Utilizzando Moq:
var stub = new Mock<IRecordProvider>();
stub.Setup(provider => provider.GetRecords()).Returns(new List<Record> {
new Record { Id = 1, Flag=false, Value="First" },
new Record { Id = 2, Flag=true, Value="Second" },
new Record { Id = 3, Flag=false, Value="Third" }
});
Ciò ottiene lo stesso comportamento dello stub codificato a mano e può essere fornito al sistema sottoposto a test in modo simile:
var processed = sut.ProcessRecord(stub.Object);
Utilizzo di una struttura di simulazione per convalidare il comportamento
I mock sono utilizzati quando è necessario verificare le interazioni tra il sistema in prova e il test raddoppia. Bisogna fare attenzione per evitare di creare test eccessivamente fragili, ma il mocking può essere particolarmente utile quando il metodo da testare è semplicemente quello di orchestrare altre chiamate.
Questo test verifica che quando viene chiamato il metodo in prova ( ProcessRecord
), viene chiamato il metodo di servizio ( UseValue
) per il Record
dove Flag==true
. Per fare ciò, imposta uno stub con dati in scatola:
var stub = new Mock<IRecordProvider>();
stub.Setup(provider => provider.GetRecords()).Returns(new List<Record> {
new Record { Id = 1, Flag=false, Value="First" },
new Record { Id = 2, Flag=true, Value="Second" },
new Record { Id = 3, Flag=false, Value="Third" }
});
Quindi imposta una simulazione che implementa l'interfaccia di IService
:
var mockService = new Mock<IService>();
mockService.Setup(service => service.UseValue(It.IsAny<string>())).Returns(true);
Questi vengono quindi forniti al sistema in prova e viene chiamato il metodo da testare.
var sut = new SystemUnderTest(mockService.Object);
var processed = sut.ProcessRecord(stub.Object);
La simulazione può quindi essere interrogata per verificare che sia stata effettuata la chiamata prevista. In questo caso, una chiamata a UseValue
, con un parametro "Second", che è il valore dal record dove Flag==true
.
mockService.Verify(service => service.UseValue("Second"));