Non stai facendo test unitari, ma test di integrazione.
-
Un test unitario garantisce che un singolo componente di un sistema funzioni come previsto.
-
Un test di integrazione combina diversi componenti e verifica che, insieme, funzionano ancora normalmente.
È vero, non puoi normalizzare i dati prima di etichettarli. Ma quando testate la normalizzazione dei dati, dovreste testare un dato etichettato. O dati che non sono stati specificamente etichettati, di proposito, per garantire che la normalizzazione fallisca con successo quando i dati non sono etichettati (o gestiscono con garbo questa situazione).
Per farlo, puoi usare il mocking. Il mocking è la tecnica in cui si sostituisce una parte di un sistema con qualcosa che fornisce solo risultati fittizi. Nei tuoi test di normalizzazione, potresti avere una simulazione che fornisce dati etichettati, un altro simulato che fornisce dati etichettati, ma in modo errato, e un altro che non etichetta nulla.
Queste tre simulazioni ti serviranno per testare la tua normalizzazione indipendentemente dall'effettivo processo di etichettatura, senza nemmeno trasportare se è già implementata o meno, né se funziona come previsto.
In questo modo, puoi testare la parte che salva i dati e poi svilupparla, quindi iniziare l'etichettatura dell'unità di prova e implementarla, infine la normalizzazione del test dell'unità e scrivere il codice corrispondente.
Una volta terminato, utilizzerai i tuoi test di integrazione per vedere se tutto il sistema funziona.
Esempio
Non capisco davvero il processo dalla tua domanda (è specifico del dominio?), quindi invento alcune illustrazioni di facile comprensione. Immaginiamo un'app che carichi i dati meteorologici (temperatura, rischio di precipitazioni, ecc.), Da diversi servizi web, la paragoni utilizzando regole specifiche, faccia previsioni su dati statistici e memorizzi il risultato in un database.
Non riesco a confrontare i dati senza caricarli e non posso creare predicati da dati statistici senza dati di sorta. Ma prima voglio attuare l'analisi statistica, perché non sono sicuro di come farlo, né di ciò che il mio cliente si aspetta di conseguenza, quindi faccio prima questa parte per aumentare le possibilità di terminare il progetto in tempo.
Per questo, posso creare un sacco di mock in base alle specifiche. Quelle schifezze mi forniranno i dati che specificherò nei test unitari stessi. Un'app non testata nel mondo reale sarebbe simile a questa:
data = [];
foreach (provider in weatherProviders)
{
data.push(provider.getTransformedData());
}
todayOverall = compareWeatherData(data); // We want to implement this first.
pastDaysOverall = loadHistoricalData();
weather = predictWeather(todayOverall, pastDaysOverall);
Un'app che usa i mock sarà simile a quella:
data = [];
foreach (provider in weatherProviders)
{
data.push(provider.getTransformedData());
}
var dataAccess = new SQLiteDatabaseAccess();
todayOverall = compareWeatherData(new BasicCompare(), data);
pastDaysOverall = loadHistoricalData(new HistoricalLoader(dataAccess));
weather = predictWeather(new WeatherOracle(dataAccess), todayOverall, pastDaysOverall);
Come vedi, compareWeatherData
prende un argomento aggiuntivo, che fa il lavoro effettivo, loadHistoricalData
ha bisogno di un oggetto che carica effettivamente i dati da un'origine dati e che, a sua volta, richiede che venga specificato un fornitore di dati, ecc. Questo ha molti vantaggi rispetto al codice precedente:
-
Se domani il cliente mi dice che la mia app deve supportare sia SQLite che MySQL, non mi interessa: farò due provider invece di uno, e specificherò il primo o il secondo a seconda di la configurazione dell'applicazione. Allo stesso modo, se l'oracolo meteorologico della prima versione è davvero brutto, posso semplicemente lavorare su un oracolo migliore, quindi spostarli facilmente.
-
Quando un'unità testa le diverse parti, posso sostituirle con una simulazione. Ad esempio, posso alimentare WeatherOracle
con FictionalDataMock: DataAccess
anziché SQLiteDatabaseAccess: DataAccess
, e in FictionalDataMock
, specificare i valori manualmente.
Quando finalmente arriva all'unità testare la riga todayOverall = compareWeatherData(new BasicCompare(), data);
, ho abbastanza potere per farlo senza nemmeno pensare ad altre parti del sistema:
/**
* Ensures that the comparison handles correctly the case where one of the data providers
* was on crack and supplied us with a temperature below Absolute Zero (-459.67°F).
*/
public test minimumTemperatureFailure()
{
dataSet1 = new TransformedDataMock(sky.snow, fromFahrenheit(23));
dataSet2 = new TransformedDataMock(sky.heavySnow, fromFahrenheit(-480)); // Too cold!
data = [ dataSet1, dataSet2 ];
actual = new BasicCompare().compare(data);
// The temperature below Absolute Zero should be ignored.
assert.areEqual(dataSet1.temperature, actual.averageTemperature);
// Since the second data provider was doing drugs, other data from it shouldn't be
// trusted neither.
assert.areEqual(dataSet1.sky, actual.mostRealisticSky);
}