Frequentemente, trabalho com programas numéricos / matemáticos, nos quais é difícil prever com antecedência o resultado exato de uma função.
Ao tentar aplicar o TDD com esse tipo de código, geralmente acho mais fácil escrever o código em teste do que escrever testes de unidade para esse código, porque a única maneira de saber o resultado esperado é aplicar o próprio algoritmo (seja no meu no papel ou no computador). Parece errado, porque estou efetivamente usando o código em teste para verificar meus testes de unidade, e não o contrário.
Existem técnicas conhecidas para escrever testes de unidade e aplicar TDD quando o resultado do código sob teste é difícil de prever?
Um exemplo (real) de código com resultados difíceis de prever:
Função weightedTasksOnTime
que, dada a quantidade de trabalho realizado por dia workPerDay
no intervalo (0, 24], o horário atual initialTime
> 0 e uma lista de tarefas taskArray
; cada uma com um tempo para concluir a propriedade time
> 0, data de vencimento due
e valor de importância importance
; retorna um valor normalizado no intervalo [0, 1] que representa a importância das tarefas que podem ser concluídas antes da due
data se cada tarefa for concluída na ordem dada por taskArray
, começando em initialTime
.
O algoritmo para implementar esta função é relativamente simples: repita as tarefas no taskArray
. Para cada tarefa, adicione time
a initialTime
. Se o novo horário < due
, adicione importance
a um acumulador. O tempo é ajustado pelo workPerDay inverso. Antes de devolver o acumulador, divida pela soma das importâncias da tarefa para normalizar.
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)
}
Acredito que o problema acima pode ser simplificado, mantendo o núcleo, removendo workPerDay
e o requisito de normalização, para fornecer:
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
}
Esta pergunta aborda situações em que o código em teste não é uma reimplementação de um algoritmo existente. Se o código é uma reimplementação, é intrinsecamente fácil prever os resultados, porque as implementações confiáveis existentes do algoritmo agem como um oráculo de teste natural.