Estou testando um método que é gerar uma coleção de objetos de dados. Quero verificar se as propriedades dos objetos estão sendo definidas corretamente. Algumas das propriedades serão definidas para a mesma coisa; outros serão configurados para um valor que depende da sua posição na coleção. A maneira natural de fazer isso parece ser com um loop. No entanto, Roy Osherove recomenda fortemente o uso da lógica em testes de unidade ( Art of Unit Testing , 178). Ele diz:
Um teste que contém lógica geralmente está testando mais de uma coisa por vez, o que não é recomendado, porque o teste é menos legível e mais frágil. Mas a lógica de teste também adiciona complexidade que pode conter um bug oculto.
Os testes devem, como regra geral, ser uma série de chamadas de método sem fluxos de controle, nem mesmo
try-catch
e com chamadas de confirmação.
No entanto, não vejo nada de errado com meu design (de que outra forma você gera uma lista de objetos de dados, alguns dos quais dependem de onde estão na sequência em que estão? - não podem exatamente gerá-los e testá-los separadamente). Existe algo que não seja amigável ao teste com meu design? Ou estou sendo muito rigidamente dedicado aos ensinamentos de Osherove? Ou há alguma mágica secreta de teste de unidade que eu não conheço que contorna esse problema? (Estou escrevendo em C # / VS2010 / NUnit, mas procurando respostas independentes do idioma, se possível.)
fonte
in
), se o teste for "Frob foi adicionado com sucesso a uma coleção existente".toString()
coleciono e comparo com o que deveria ser. Simples e funciona.Respostas:
TL; DR:
A primeira coisa a ser testada é sobre o dogma ser inútil. Gosto de ler The Way of Testivus, que aponta alguns problemas com o dogma de uma maneira alegre.
Se o teste precisar ser escrito de alguma forma, escreva dessa maneira. Tentar forçar o teste a um layout de teste idealizado ou não tê-lo, não é uma coisa boa. Ter um teste hoje que testa é melhor do que ter um teste "perfeito" mais tarde.
Também vou apontar para o pouco no teste feio:
Estes podem ser considerados truques para aqueles que têm seguido por um longo tempo ... e eles se tornam arraigados na maneira de pensar e escrever testes. Para pessoas que não estiveram e estão tentando chegar a esse ponto, os lembretes podem ser úteis (acho até que relê-los me ajuda a evitar ficar preso em algum dogma).
Considere que, ao escrever um teste feio, se o código puder ser uma indicação de que o código está tentando fazer muito também. Se o código que você está testando for muito complexo para ser exercitado adequadamente escrevendo um teste simples, considere dividir o código em partes menores que podem ser testadas com os testes mais simples. Não se deve escrever um teste de unidade que faça tudo (pode não ser um teste de unidade ). Assim como 'objetos de Deus' são ruins, 'testes de unidade de Deus' também são ruins e devem ser indicações para voltar e olhar o código novamente.
Você deve poder exercitar todo o código com uma cobertura razoável por meio de testes simples. Testes que fazem mais testes de ponta a ponta que lidam com perguntas maiores ("Eu tenho esse objeto, empacotado em xml, enviado ao serviço da web, através das regras, de volta e não empacotado") é um excelente teste - mas certamente não é é um teste de unidade (e cai no domínio de teste de integração - mesmo que tenha zombado dos serviços que chama e customizado nos bancos de dados de memória para fazer o teste). Ele ainda pode usar a estrutura do XUnit para teste, mas a estrutura de teste não o torna um teste de unidade.
fonte
Estou adicionando uma nova resposta porque minha perspectiva é diferente de quando escrevi a pergunta e a resposta originais; não faz sentido combiná-los juntos em um.
Eu disse na pergunta original
Foi aqui que eu errei. Depois de fazer a programação funcional do ano passado, agora percebo que eu só precisava de uma operação de coleta com um acumulador. Então eu poderia escrever minha função como uma função pura que funcionava em uma coisa e usar alguma função de biblioteca padrão para aplicá-la à coleção.
Portanto, minha nova resposta é: use técnicas de programação funcional e você evitará esse problema inteiramente a maior parte do tempo. Você pode escrever suas funções para operar com itens únicos e aplicá-las apenas a coleções de itens no último momento. Mas se forem puros, você poderá testá-los sem fazer referência a coleções.
Para uma lógica mais complexa, use testes baseados em propriedades . Quando eles têm lógica, ela deve ser menor e inversa à lógica do código em teste, e cada teste verifica muito mais do que um teste de unidade baseado em casos que a pequena quantidade de lógica vale a pena.
Acima de tudo, incline-se sempre nos seus tipos . Obtenha os tipos mais fortes que puder e use-os para sua vantagem. Isso reduzirá o número de testes que você precisa escrever em primeiro lugar.
fonte
Não tente testar muitas coisas ao mesmo tempo. Cada uma das propriedades de cada objeto de dados na coleção é demais para um teste. Em vez disso, recomendo:
Fazer dessa maneira torna os testes pequenos o suficiente para que deixar de fora os loops não pareça doloroso. Exemplo de C # / Unidade, com o método em teste
ICollection<Foo> generateFoos(uint numberOfFoos)
:Se você está acostumado ao paradigma "teste de unidade plana" (sem estruturas / lógica aninhadas), esses testes parecem bastante limpos. Assim, a lógica é evitada nos testes, identificando o problema original como tentando testar muitas propriedades ao mesmo tempo, em vez de não ter loops.
fonte