Lavoro spesso con programmi molto numerici / matematici, dove è difficile prevedere in anticipo il risultato esatto di una funzione.
Nel tentativo di applicare TDD con questo tipo di codice, trovo spesso la scrittura del codice in prova molto più semplice rispetto alla scrittura di unit test per quel codice, perché l'unico modo che conosco per trovare il risultato atteso è applicare l'algoritmo stesso (sia nel mio testa, su carta o al computer). Questo sembra sbagliato, perché sto effettivamente usando il codice in prova per verificare i test delle mie unità, invece del contrario.
Esistono tecniche note per scrivere unit test e applicare TDD quando è difficile prevedere il risultato del codice in prova?
Un esempio (reale) di codice con risultati difficili da prevedere:
Una funzione weightedTasksOnTime
che, data una quantità di lavoro svolto al giorno workPerDay
nell'intervallo (0, 24], l'ora corrente initialTime
> 0 e un elenco di attività taskArray
; ognuna con un tempo per completare la proprietà time
> 0, data di scadenza due
e valore di importanza importance
; restituisce un valore normalizzato nell'intervallo [0, 1] che rappresenta l'importanza delle attività che possono essere completate prima della due
data se ciascuna attività viene completata nell'ordine indicato da taskArray
, a partire da initialTime
.
L'algoritmo per implementare questa funzione è relativamente semplice: iterare le attività in taskArray
. Per ogni attività, aggiungi time
a initialTime
. Se il nuovo tempo < due
, aggiungi importance
a un accumulatore. Il tempo viene regolato da inverso workPerDay. Prima di restituire l'accumulatore, dividere per somma delle importazioni di attività da 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 affronta situazioni in cui il codice in esame non è una reimplementazione di un algoritmo esistente. Se il codice è una reimplementazione, ha intrinsecamente facili risultati da prevedere, poiché le implementazioni di fiducia esistenti dell'algoritmo fungono da naturale oracolo del test.