Często pracuję z programami numerycznymi / matematycznymi, w których dokładny wynik funkcji jest trudny do przewidzenia z góry.
Próbując zastosować TDD z tego rodzaju kodem, często uważam, że pisanie testowanego kodu jest znacznie łatwiejsze niż pisanie testów jednostkowych dla tego kodu, ponieważ jedynym sposobem na znalezienie oczekiwanego wyniku jest zastosowanie samego algorytmu (czy w moim głowa, na papierze lub przy komputerze). To wydaje się błędne, ponieważ skutecznie używam testowanego kodu do weryfikacji moich testów jednostkowych, zamiast na odwrót.
Czy znane są techniki pisania testów jednostkowych i stosowania TDD, gdy trudno jest przewidzieć wynik testowanego kodu?
(Prawdziwy) przykład kodu z trudnymi do przewidzenia wynikami:
Funkcja, weightedTasksOnTime
która biorąc pod uwagę ilość pracy wykonanej dziennie workPerDay
w zakresie (0, 24], aktualny czas initialTime
> 0 i listę zadań taskArray
; każde z czasem do ukończenia właściwości time
> 0, terminem due
i wartością ważności importance
; zwraca znormalizowana wartość z zakresu [0, 1] reprezentująca znaczenie zadań, które można wykonać przed ich due
datą, jeżeli każde zadanie zostanie wykonane w kolejności podanej przez taskArray
, począwszy od initialTime
.
Algorytm implementujący tę funkcję jest stosunkowo prosty: iteruj po zadaniach w taskArray
. Dla każdego zadania, dodać time
do initialTime
. Jeśli nowy czas < due
, dodaj importance
do akumulatora. Czas jest regulowany przez odwrotną pracęPerDay. Przed zwróceniem akumulatora podziel przez sumę ważności zadań w celu normalizacji.
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)
}
Wierzę, że powyższy problem można uprościć, zachowując jego rdzeń, usuwając workPerDay
i wymagając normalizacji, aby dać:
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
}
To pytanie dotyczy sytuacji, w których testowany kod nie jest ponowną implementacją istniejącego algorytmu. Jeśli kod jest ponowną implementacją, z natury ma on łatwe do przewidzenia wyniki, ponieważ istniejące zaufane implementacje algorytmu działają jak naturalna wyrocznia testowa.