Acionado por esse segmento , eu (novamente) estou pensando em finalmente usar testes de unidade em meus projetos. Alguns pôsteres dizem algo como "Os testes são legais, se são bons testes". Minha pergunta agora: o que são testes "bons"?
Em meus aplicativos, a parte principal geralmente é algum tipo de análise numérica, dependendo de grandes quantidades de dados observados e resultando em uma função de ajuste que pode ser usada para modelar esses dados. Achei especialmente difícil construir testes para esses métodos, uma vez que o número de entradas e resultados possíveis é muito grande para testar todos os casos, e os métodos em si são geralmente muito longos e não podem ser facilmente refatorados sem sacrificar o desempenho. Estou especialmente interessado em "bons" testes para esse tipo de método.
fonte
Respostas:
A Arte do Teste de Unidade tem o seguinte a dizer sobre testes de unidade:
e depois acrescenta que deve ser totalmente automatizado, confiável, legível e sustentável.
Eu recomendo fortemente a leitura deste livro, se você ainda não o fez.
Na minha opinião, tudo isso é muito importante, mas os três últimos (confiáveis, legíveis e mantidos), especialmente, como se seus testes tivessem essas três propriedades, então seu código também as possui.
fonte
It should run at the push of a button
, isso significa que um teste de unidade não deve exigir contêineres (servidor de aplicativos) em execução (para a unidade que está sendo testada) ou uma conexão de recursos (como banco de dados, serviços da web externos etc.)? Estou confuso sobre quais partes de um aplicativo devem ser testadas em unidade e quais não. Foi-me dito que os testes de unidade não deveriam depender da conexão com o banco de dados e dos contêineres em execução, e talvez usassem maquetes.Um bom teste de unidade não reflete a função que está testando.
Como um exemplo bastante simplificado, considere que você tem uma função que retorna uma média de dois int. O teste mais abrangente chamaria a função e verificaria se um resultado é de fato uma média. Isso não faz nenhum sentido: você está espelhando (replicando) a funcionalidade que está testando. Se você cometeu um erro na função principal, cometerá o mesmo erro no teste.
Em outras palavras, se você se replicar na funcionalidade principal do teste de unidade, é um sinal provável de que está desperdiçando seu tempo.
fonte
Bons testes de unidade são essencialmente a especificação na forma executável:
Eu descobri que o desenvolvimento orientado a testes é muito adequado para rotinas de bibliotecas, pois você essencialmente escreve a API primeiro e depois a implementação real.
fonte
para TDD, "bom" testa os recursos de teste que o cliente deseja ; os recursos não correspondem necessariamente às funções e os cenários de teste não devem ser criados pelo desenvolvedor no vácuo
no seu caso - suponho - o 'recurso' é que a função de ajuste modela os dados de entrada dentro de uma certa tolerância a erros. Como não tenho ideia do que você realmente está fazendo, estou inventando algo; espero que seja análogo.
Exemplo de história:
Então você vai falar com os pilotos (e com o computador alvo, se for sensível). Primeiro você fala sobre o que é 'normal', depois fala sobre o anormal. Você descobre o que realmente importa nesse cenário, o que é comum, o que é improvável e o que é apenas possível.
Digamos que normalmente você terá uma janela de meio segundo em sete canais de dados de telemetria: velocidade, inclinação, rotação, guinada, vetor de destino, tamanho de destino e velocidade de destino, e esses valores serão constantes ou mudarão linearmente. De forma anormal, você pode ter menos canais e / ou os valores podem estar mudando rapidamente. Então, juntos, você faz alguns testes, como:
Agora, você deve ter notado que não há cenário para a situação específica descrita na história. Acontece que, depois de conversar com o cliente e outras partes interessadas, esse objetivo na história original era apenas um exemplo hipotético. Os testes reais saíram da discussão que se seguiu. Isso pode acontecer. A história deve ser reescrita, mas não precisa ser [porque a história é apenas um espaço reservado para uma conversa com o cliente].
fonte
Crie testes para casos de canto, como um conjunto de testes contendo apenas o número mínimo de entradas (possível 1 ou 0) e alguns casos padrão. Esses testes de unidade não substituem os testes de aceitação completos, nem deveriam ser.
fonte
Eu já vi muitos casos em que as pessoas investem uma enorme quantidade de esforço escrevendo testes para códigos raramente inseridos, e não escrevendo testes para códigos inseridos com frequência.
Antes de se sentar para escrever qualquer teste, você deve observar algum tipo de gráfico de chamada, para garantir uma cobertura adequada.
Além disso, não acredito em escrever testes apenas para dizer "Sim, nós testamos isso". Se eu estiver usando uma biblioteca que é descartada e permanecerá imutável, não vou perder um dia escrevendo testes para garantir que as entranhas de uma API que nunca mudem funcionem conforme o esperado, mesmo que determinadas partes dela alto em um gráfico de chamada. Testes que consomem a referida biblioteca (meu próprio código) apontam isso.
fonte
Não é tão TDD, mas depois de entrar no controle de qualidade, você pode melhorar seus testes configurando casos de teste para reproduzir os erros que surgirem durante o processo de controle de qualidade. Isso pode ser particularmente valioso quando você busca um suporte de longo prazo e começa a chegar a um local em que corre o risco de pessoas inadvertidamente reintroduzirem erros antigos. Ter um teste para capturar isso é particularmente valioso.
fonte
Eu tento fazer com que todos os testes testem apenas uma coisa. Eu tento dar a cada teste um nome como shouldDoSomething (). Eu tento testar o comportamento, não a implementação. Eu só testei métodos públicos.
Normalmente, tenho um ou alguns testes de sucesso e, em seguida, talvez alguns testes de falha, por método público.
Eu uso muito maquetes. Uma boa estrutura de simulação provavelmente seria bastante útil, como o PowerMock. Embora eu não esteja usando ainda.
Se a classe A usar outra classe B, eu adicionaria uma interface, X, para que A não use B diretamente. Depois, criava o XMockup de mock-up e o usava em vez de B nos meus testes. Isso realmente ajuda a acelerar a execução do teste, reduzindo a complexidade do teste e também reduz o número de testes que eu escrevo para A, pois não preciso lidar com as peculiaridades de B. Posso, por exemplo, testar se A chama X.someMethod (). em vez de um efeito colateral de chamar B.someMethod ().
Mantenha seu código de teste limpo também.
Ao usar uma API, como uma camada de banco de dados, eu a simulava e permitia que a simulação gerasse uma exceção em todas as oportunidades possíveis sob comando. Em seguida, executo os testes um sem lançar e o em loop, sempre lançando uma exceção na próxima oportunidade até que o teste seja novamente bem-sucedido. Um pouco como os testes de memória disponíveis para o Symbian.
fonte
Vejo que Andry Lowry já publicou as métricas de teste de unidade de Roy Osherove; mas parece que ninguém apresentou o conjunto (complementar) que o tio Bob fornece em Clean Code (132-133). Ele usa a sigla PRIMEIRO (aqui com meus resumos):
fonte