Estratégias para teste de unidade e desenvolvimento orientado a teste

16

Sou um grande defensor do desenvolvimento orientado a testes em computação científica. Sua utilidade na prática é simplesmente impressionante e realmente alivia os problemas clássicos que os desenvolvedores de código conhecem. No entanto, existem dificuldades inerentes ao teste de códigos científicos que não são encontrados na programação geral; portanto, os textos TDD não são muito úteis como tutoriais. Por exemplo:

  • Em geral, você não sabe a resposta exata para um determinado problema complexo a priori; então, como você pode escrever um teste?

  • O grau de paralelismo muda; Recentemente, encontrei um bug em que o uso de tarefas MPI como um múltiplo de 3 falharia, mas um múltiplo de 2 funcionava. Além disso, estruturas de teste comuns não parecem muito compatíveis com o MPI devido à própria natureza do MPI - você precisa executar novamente um binário de teste para alterar o número de tarefas.

  • Os códigos científicos geralmente têm muitas peças fortemente acopladas, interdependentes e intercambiáveis. Todos nós vimos o código legado e sabemos como é tentador renunciar a um bom design e usar variáveis ​​globais.

  • Freqüentemente, um método numérico pode ser um "experimento" ou o codificador não entende completamente como funciona e está tentando entendê-lo, portanto, antecipar resultados é impossível.

Alguns exemplos de testes que escrevo para código científico:

  • Para integradores de tempo, use um ODE simples com uma solução exata e teste se o seu integrador o soluciona com uma determinada precisão, e a ordem da precisão está correta testando com diferentes tamanhos de etapas.

  • Testes de estabilidade zero: verifique se um método com 0 limite / condições iniciais permanece em 0.

  • Testes de interpolação: dada uma função linear, garanta que uma interpolação esteja correta.

  • Validação herdada: isole um pedaço de código em um aplicativo herdado que esteja correto e retire alguns valores discretos para usar nos testes.

Ainda é frequente que eu não consiga descobrir como testar adequadamente um determinado pedaço de código, além da tentativa e erro manual. Você pode fornecer alguns exemplos de testes que você escreve para código numérico e / ou estratégias gerais para testar software científico?

Aurelius
fonte
Você poderia, por favor, esclarecer o que você entende por testes de interpolação?
Dmitry Kabanov

Respostas:

8

Método de soluções fabricadas .

Verifique através de estudos de refinamento que o método atinge a ordem teórica de precisão.

Conservação de resposta. Reprodução de soluções bit a bit e a norma.

Bill Barth
fonte
Eu quis mencionar MMS no post original; é bom para verificação de código, mas, de uma perspectiva de teste de unidade, é totalmente inútil. Se esses testes falharem, não há nenhuma pista sobre onde ou por quê.
Aurelius
3
@Aurelius: Mas é uma ótima estratégia para o desenvolvimento orientado a testes! Para códigos de álgebra PDE / ODE / Linear, você deve escrever testes MMS muito pequenos que podem ser executados em menos de um segundo. Quando você faz uma alteração, você as executa. Se eles quebrarem, você fez algo errado! Você ficaria surpreso com o quanto um problema de pode lhe dizer (ou o que seja). 2×2×2
Bill Barth
Muita literatura que vi no MMS são basicamente soluções globais, por exemplo, para problemas de CFD, uma solução fabricada pode ser uma análise de aerofólio. Quando esse teste falha, na melhor das hipóteses, você reduziu o culpado para 5.000 linhas de código, por isso é bastante inútil para o TDD - você não tem idéia de onde ocorre a falha real. Concordo que um problema 2x2x2 é extremamente valioso e eu pessoalmente os uso muito. Mas é bastante comum encontrar problemas que só aparecem em sistemas maiores; Na verdade, encontrei recentemente um bug do compilador ifort que só se manifestava em grandes problemas.
Aurelius
@Aurelius: Nenhum argumento aqui. Você deve ter uma variedade de testes e executá-los com frequência.
Bill Barth
@Aurelius Pelo valor de face, o MMS não é um teste de unidade, mas um teste funcional ou de aceitação (ou seja, de todo o sistema). No entanto, os códigos geralmente têm estágios separados (ou podem ser divididos entre eles). por exemplo, advecção, pressão, viscosidade. Pode-se então testar apenas um desses estágios (uma "unidade"). Da mesma forma, um código pode ser testado sem um BC e depois com um. Um amigo fez seu doutorado em testes de unidade e ele considerou o maior benefício que foi forçado a dividir seu programa em unidades, para que ele possa ser testado em unidade ... talvez isso seja mais aplicável aqui do que parece à primeira vista (e de outras maneiras que eu não conheço).
hyperpallium
6

Bill já listou alguns métodos que abordam suas preocupações.

Dirigindo-se ao seu terceiro ponto, não, não há razão para introduzir acoplamentos fortes entre as peças. Exatamente o oposto: se suas funções ou classes tiverem interfaces bem definidas, será muito mais fácil trocar, por exemplo, um solucionador linear por outro, ou um esquema de escalonamento no tempo. Apenas resista e você poderá testar esses componentes separadamente. Fazemos isso com deal.II há décadas.

Para seu quarto ponto: se seu método é um experimento, seus experimentos com o método constituem um teste. Contanto que você não tenha análise, será necessário realizar esses resultados da melhor forma possível. Mas, geralmente, você tem uma expectativa, por exemplo, da ordem de um método, ou saberia que é exato para uma certa classe de soluções, por exemplo, polinômios até um certo grau. A verificação dessas informações deve fazer parte de seus experimentos e, à medida que a análise melhora, é possível adicionar testes.

Guido Kanschat
fonte
11
Para adicionar à resposta de Guido, a experiência da qual ele fala é codificada nos ~ 3.000 testes que executamos no acordo.II após cada alteração: dealii.org/developer/development/… . Sobre a questão do que fazer se você não souber a resposta exata: escreva um teste de qualquer maneira e compare-o hoje com a resposta de ontem (ou sempre que você escreveu o teste). Ter uma maneira de detectar alterações na saída de um código é valioso, mesmo que você não saiba se eles fizeram a resposta incorreta ou corrigiram uma resposta incorreta anteriormente.
Wolfgang Bangerth
3

Eu encontrei recentemente esta tese sobre TDD em Ciência da Computação. Ainda não li, por isso não faço ideia se é bom, mas espero que possa ajudar.

http://cyber.ua.edu/files/2014/12/u0015_0000001_0001551.pdf

revistas
fonte
11
Examinei algumas das introdução e conclusões e, assumindo um nível de qualidade a par da tese de doutorado padrão, isso explica o processo (em um nível mais alto) e fornece medições reais quanto à sua eficácia. Eu acho que isso é um achado.
Godric Seer
O link está morto. Você quis dizer: Nanthaamornphong, A. “A eficácia das técnicas de desenvolvimento e refatoração orientadas a testes na ciência da computação e no desenvolvimento de software de engenharia”. PhD diss., Uni. Alabama (2014).
AlQuemist