Descobri que boa parte da minha programação de ciências computacionais tem requisitos de teste que não são cobertos pelas estruturas de teste padrão:
Teste de tempo de computação
- Para garantir que os algoritmos não fiquem mais lentos. Eu poderia fazer algo parecido,
assureSmallerEqual(RuntimeWrapper(algorithm),53)
mas gostaria que o limite de 53 segundos fosse reduzido continuamente, enquanto trabalho no algoritmo, ou seja, algo comoassureSmallerEqual(RuntimeWrapper(algorithm),'previousbest+noisetolerance')
- Para garantir que os algoritmos não fiquem mais lentos. Eu poderia fazer algo parecido,
Teste de performance
- Para garantir que um algoritmo que encontrou anteriormente uma boa aproximação a uma solução analítica ainda encontre uma solução que seja pelo menos tão boa ou melhor. Novamente, isso pode ser emulado por um teste de integração padrão, mas eu gostaria que a tolerância diminuísse continuamente à medida que o algoritmo se tornasse cada vez melhor. Pense em substituir
assureAlmostEqual(foo(),1,places=3)
porassureAlmostEqual(foo(),1,places='previousbest')
- Para garantir que um algoritmo que encontrou anteriormente uma boa aproximação a uma solução analítica ainda encontre uma solução que seja pelo menos tão boa ou melhor. Novamente, isso pode ser emulado por um teste de integração padrão, mas eu gostaria que a tolerância diminuísse continuamente à medida que o algoritmo se tornasse cada vez melhor. Pense em substituir
Teste de requisitos físicos
- Para garantir que os algoritmos não precisem repentinamente de mais memória / espaço no disco rígido. Muito parecido com 1.
Teste de requisitos abstratos
- Para garantir que um algoritmo que funcionou bem com aproximações quadráticas não precise subitamente de aproximações cúbicas, ou que um algoritmo que funcionou bem com o tempo da etapa 0.1 não precise de 0,01 para estabilidade. Novamente, eles podem ser emulados por testes de integração padrão, mas o objetivo é lembrar qual foi o menor parâmetro de requisito que atingiu um determinado objetivo; portanto, isso exigiria muita atualização manual. Por exemplo, se
foo(10)
anteriormente não houvesse exceções, gostaria que a estrutura certificasse quefoo(10)
ainda funcionasse e também tentasse sefoo(9)
funcionasse agora (nesse caso, todos os testes futuros garantiriam quefoo(9)
ainda funcionem).
- Para garantir que um algoritmo que funcionou bem com aproximações quadráticas não precise subitamente de aproximações cúbicas, ou que um algoritmo que funcionou bem com o tempo da etapa 0.1 não precise de 0,01 para estabilidade. Novamente, eles podem ser emulados por testes de integração padrão, mas o objetivo é lembrar qual foi o menor parâmetro de requisito que atingiu um determinado objetivo; portanto, isso exigiria muita atualização manual. Por exemplo, se
Alguém poderia argumentar que o que estou pedindo não descreve testes no sentido de teste de unidade / integração, já que tempos de execução maiores, por exemplo, podem ser aceitáveis em troca de outras melhorias.
Na prática, no entanto, sei que economizaria muito tempo de depuração se tivesse a funcionalidade de teste acima, porque em 95% dos casos os requisitos e o desempenho foram incorretos devido a erros que introduzi. De fato, sei que muitos erros que encontrei (depois de muito tempo desperdiçado na verificação de meu próprio código) com bibliotecas de software numéricas externas poderiam ter sido evitados trivialmente se os testes acima fossem aplicados rigorosamente.
PS
A pergunta de nome semelhante /programming/34982863/framework-for-regression-testing-of-numerical-code não é uma duplicata, pois descreve a funcionalidade que é mais facilmente alcançável com estruturas de teste de regressão padrão.
A pergunta Estratégias para teste de unidade e desenvolvimento orientado a teste pede estratégias em oposição a uma estrutura que ajude a implementá-las (e as estratégias solicitadas / fornecidas nas respostas são diferentes do que descrevo aqui, na minha opinião).
Respostas:
1. Esse tipo de teste parece-me mal definido porque sua condição de teste está ligada à máquina específica na qual você fez os testes em desenvolvimento. Um dos pontos de teste é que a execução dos testes no meu laptop informa se há algo errado com o código ou o ambiente que eu configurei. Os 53 segundos são específicos para sua máquina de desenvolvimento e o tempo de execução também aumentará se a máquina de teste estiver sob carga de outras cargas de trabalho ou usuários. Eu não esperaria que estruturas de teste resolvessem isso: "a função é executada na entrada em menos de 53 segundos" não é apenas uma especificação de correção muito boa.
2. Eu acho isso ambíguo e indesejável do ponto de vista dos testes de software pelas mesmas razões 1 : você perde a justificativa de aprovação ou reprovação dos testes de software.
3. Isso é bastante comum, deixe-me descrever uma solução. Não é exatamente o trabalho de uma estrutura de teste, mas você pode usar uma ferramenta separada, conforme descrito na pergunta do Unix SE, Limitar o uso de memória para um único processo Linux . Uma ferramenta padrão para tentar primeiro é o
ulimit
comando inbash
, que permite executar um processo e garantir que ele trava se tentar, por exemplo, alocar muita memória. Portanto, se você executar oruntests
script com um limite de memória, ele travará e a estrutura de teste poderá lidar com isso como uma falha de teste regular.4. A maioria dos frameworks de teste não pensam da unidade de testes desta maneira em tudo . O conjunto de testes é executado (por exemplo, antes de confirmar o código para o mestre ou antes da implantação) e o resultado é sim ou não, indicando se funciona. As estruturas de teste não consideram parte de seu trabalho, por exemplo, rastrear o progresso dos recursos, e isso não é o que geralmente é o teste. O que você faria aqui é escrever dois testes
expect_succeeds(foo(10)); expect_fails(foo(9))
. A cada vez, os dois testes são executados e os sucessos e as falhas esperadas passam. Quando você implementafoo(9)
e é bem-sucedido, o teste de expectativa de falha agora falha, então você reescreveexpect_succeeds(foo(9))
, e esse é um recurso absolutamente padrão de todas as estruturas. Mas você deve ser explícito sobre o comportamento que espera, porque, caso contrário, isso vai contra as idéias básicas dos testes de software.performs_better(foo_A(), foo_B())
fonte