Lavoro spesso con programmi numerici / matematici, in cui è difficile prevedere in anticipo il risultato esatto di una funzione.
Nel tentativo di applicare TDD con questo tipo di codice, spesso trovo che scrivere il codice sotto test sia molto più semplice rispetto alla scrittura di unit test per quel codice, perché l'unico modo per sapere il risultato atteso è applicare l'algoritmo stesso ( se nella mia testa, sulla carta o dal computer). Questo sembra sbagliato, perché sto usando efficacemente il codice sotto test per verificare i miei test unitari, invece del contrario.
Esistono tecniche note per scrivere test di unità e applicare TDD quando il risultato del codice in esame è difficile da prevedere?
Un esempio (reale) di codice con risultati difficili da prevedere:
Una funzione weightedTasksOnTime
che, data una quantità di lavoro al giorno workPerDay
nell'intervallo (0, 24), il tempo corrente initialTime
> 0 e un elenco di compiti taskArray
; ciascuno con un tempo per completare la proprietà time
> 0, la data di scadenza due
e il valore di importanza importance
; restituisce un valore normalizzato nell'intervallo [0, 1] che rappresenta l'importanza delle attività che possono essere completate prima del loro due
data se ogni attività è completata nell'ordine dato da taskArray
, a partire da initialTime
.
L'algoritmo per implementare questa funzione è relativamente semplice: iterare su attività in taskArray
. Per ogni attività, aggiungi time
a initialTime
. Se la nuova ora < due
, aggiungi importance
a un accumulatore. Il tempo viene regolato da workPerDay inverso. Prima di restituire l'accumulatore, dividi per somma delle operazioni da importare per normalizzare.
function weightedTasksOnTime(workPerDay, initialTime, taskArray) {
let simulatedTime = initialTime
let accumulator = 0;
for (task in taskArray) {
simulatedTime += task.time * (24 / workPerDay)
if (simulatedTime < task.due) {
accumulator += task.importance
}
}
return accumulator / totalImportance(taskArray)
}
Credo che il problema di cui sopra possa essere semplificato, pur mantenendo il suo nucleo, rimuovendo workPerDay
e il requisito di normalizzazione, per dare:
function weightedTasksOnTime(initialTime, taskArray) {
let simulatedTime = initialTime
let accumulator = 0;
for (task in taskArray) {
simulatedTime += task.time
if (simulatedTime < task.due) {
accumulator += task.importance
}
}
return accumulator
}
Questa domanda riguarda situazioni in cui il codice in prova non è una reimplementazione di un algoritmo esistente. Se il codice è una reimplementazione, è intrinsecamente facile prevedere i risultati, poiché le implementazioni attendibili esistenti dell'algoritmo fungono da oracolo di prova naturale.