Je travaille fréquemment avec des programmes très numériques / mathématiques, où le résultat exact d'une fonction est difficile à prédire à l'avance.
En essayant d'appliquer TDD avec ce type de code, je trouve souvent que l'écriture du code sous test est beaucoup plus facile que l'écriture de tests unitaires pour ce code, car le seul moyen de connaître le résultat attendu est d'appliquer l'algorithme lui-même (que ce soit dans mon cas). tête, sur papier ou par ordinateur). Cela me semble erroné, car j’utilise efficacement le code testé pour vérifier mes tests unitaires, et non l’inverse.
Existe-t-il des techniques connues pour écrire des tests unitaires et appliquer TDD lorsque le résultat du code testé est difficile à prévoir?
Un exemple (réel) de code avec des résultats difficiles à prévoir:
Fonction weightedTasksOnTime
qui, en fonction de la quantité de travail effectué par jour workPerDay
dans la plage (0, 24], de l'heure actuelle initialTime
> 0 et d'une liste de tâches taskArray
, chacune avec un délai pour terminer la propriété time
> 0, la date d'échéance due
et la valeur d'importance importance
; renvoie une valeur normalisée dans l'intervalle [0, 1] représentant l'importance des tâches pouvant être terminées avant leur due
date si chaque tâche est complétée dans l'ordre indiqué en taskArray
commençant à initialTime
.
L'algorithme pour implémenter cette fonction est relativement simple: itérer sur des tâches dans taskArray
. Pour chaque tâche, ajoutez time
à initialTime
. Si la nouvelle heure < due
, ajoutez importance
à un accumulateur. Le temps est ajusté par workPerDay inverse. Avant de renvoyer l'accumulateur, divisez par la somme des importances de tâches à normaliser.
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)
}
Je crois que le problème ci-dessus peut être simplifié, tout en conservant son cœur, en supprimant workPerDay
et en normalisant l'exigence, pour donner:
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
}
Cette question concerne les situations dans lesquelles le code testé n'est pas une nouvelle implémentation d'un algorithme existant. Si le code est une réimplémentation, il est intrinsèquement facile de prédire les résultats, car les implémentations fiables de l'algorithme agissent comme un oracle de test naturel.