O código duplicado é um cheiro no código de teste de unidade tanto quanto em outro código. Se você tiver código duplicado nos testes, será mais difícil refatorar o código de implementação porque você tem um número desproporcional de testes para atualizar. Os testes devem ajudá-lo a refatorar com confiança, em vez de ser uma grande carga que impede seu trabalho no código que está sendo testado.
Se a duplicação estiver em uma configuração de fixação, considere fazer mais uso do setUp
método ou fornecer mais (ou mais flexíveis) Métodos de Criação .
Se a duplicação está no código que manipula o SUT, pergunte-se por que vários testes chamados de “unidade” estão exercendo exatamente a mesma funcionalidade.
Se a duplicação estiver nas afirmações, talvez você precise de algumas afirmações personalizadas . Por exemplo, se vários testes tiverem uma sequência de afirmações como:
assertEqual('Joe', person.getFirstName())
assertEqual('Bloggs', person.getLastName())
assertEqual(23, person.getAge())
Então, talvez você precise de um único assertPersonEqual
método para poder escrever assertPersonEqual(Person('Joe', 'Bloggs', 23), person)
. (Ou talvez você simplesmente precise sobrecarregar o operador de igualdade Person
.)
Como você mencionou, é importante que o código de teste seja legível. Em particular, é importante que a intenção de um teste seja clara. Eu acho que se muitos testes parecem quase iguais (por exemplo, três quartos das linhas iguais ou virtualmente iguais) é difícil detectar e reconhecer as diferenças significativas sem ler e compará-las cuidadosamente. Portanto, acho que a refatoração para remover a duplicação ajuda na legibilidade, porque cada linha de cada método de teste é diretamente relevante para o propósito do teste. Isso é muito mais útil para o leitor do que uma combinação aleatória de linhas que são diretamente relevantes e linhas que são apenas clichês.
Dito isso, às vezes os testes exercem situações complexas que são semelhantes, mas ainda assim significativamente diferentes, e é difícil encontrar uma boa maneira de reduzir a duplicação. Use o bom senso: se você sentir que os testes são legíveis e deixar sua intenção clara, e se sentir confortável com a talvez necessidade de atualizar mais do que um número teoricamente mínimo de testes ao refatorar o código invocado pelos testes, aceite a imperfeição e mude para algo mais produtivo. Você sempre pode voltar e refatorar os testes mais tarde, quando a inspiração bater!
A legibilidade é mais importante para os testes. Se um teste falhar, você deseja que o problema seja óbvio. O desenvolvedor não deve ter que percorrer muitos códigos de teste altamente fatorados para determinar exatamente o que falhou. Você não quer que seu código de teste se torne tão complexo que precise escrever testes de unidade.
No entanto, eliminar a duplicação geralmente é uma coisa boa, desde que não obscureça nada, e eliminar a duplicação em seus testes pode levar a uma API melhor. Apenas certifique-se de não ultrapassar o ponto de retornos decrescentes.
fonte
O código de implementação e os testes são animais diferentes e as regras de fatoração se aplicam de maneira diferente a eles.
Código ou estrutura duplicados são sempre um cheiro no código de implementação. Quando você começa a ter um padrão em implementação, precisa revisar suas abstrações.
Por outro lado, o código de teste deve manter um nível de duplicação. A duplicação no código de teste atinge dois objetivos:
Eu tendo a ignorar a duplicação trivial no código de teste, desde que cada método de teste permaneça menor que cerca de 20 linhas. Gosto quando o ritmo de configuração-execução-verificação é aparente nos métodos de teste.
Quando a duplicação surge na parte "verificar" dos testes, costuma ser benéfico definir métodos de asserção personalizados. Claro, esses métodos ainda devem testar uma relação claramente identificada que pode ser tornada aparente no nome do método:
assertPegFitsInHole
-> bom,assertPegIsGood
-> ruim.Quando os métodos de teste se tornam longos e repetitivos, às vezes acho útil definir modelos de teste para preencher as lacunas que usam alguns parâmetros. Em seguida, os métodos de teste reais são reduzidos a uma chamada ao método de modelo com os parâmetros apropriados.
Quanto a muitas coisas em programação e teste, não há uma resposta clara. Você precisa desenvolver um gosto, e a melhor maneira de fazer isso é cometendo erros.
fonte
Concordo. A troca existe, mas é diferente em lugares diferentes.
É mais provável que eu refatore código duplicado para configurar o estado. Mas é menos provável que refatore a parte do teste que realmente exercita o código. Dito isso, se exercitar o código sempre exige várias linhas de código, então posso pensar que é um cheiro e refatorar o código real em teste. E isso vai melhorar a legibilidade e a manutenção do código e dos testes.
fonte
Você pode reduzir a repetição usando vários tipos diferentes de métodos de utilitário de teste .
Sou mais tolerante com a repetição no código de teste do que no código de produção, mas às vezes fico frustrado com isso. Quando você altera o design de uma classe e tem que voltar e ajustar 10 métodos de teste diferentes que executam as mesmas etapas de configuração, é frustrante.
fonte
Jay Campos cunhou a frase que "DSLs deve ser úmido, não seca", onde DAMP meios descritiva e frases significativas . Acho que o mesmo se aplica aos testes também. Obviamente, muita duplicação é ruim. Mas remover a duplicação a todo custo é ainda pior. Os testes devem atuar como especificações reveladoras de intenção. Se, por exemplo, você especificar o mesmo recurso de vários ângulos diferentes, uma certa quantidade de duplicação é esperada.
fonte
EU AMO rspec por causa disso:
Tem 2 coisas para ajudar -
grupos de exemplos compartilhados para testar o comportamento comum.
você pode definir um conjunto de testes e então 'incluir' esse conjunto em seus testes reais.
contextos aninhados.
você pode essencialmente ter um método de 'configuração' e 'desmontagem' para um subconjunto específico de seus testes, não apenas para cada um na classe.
Quanto mais cedo .NET / Java / outras estruturas de teste adotarem esses métodos, melhor (ou você pode usar IronRuby ou JRuby para escrever seus testes, que eu pessoalmente acho que é a melhor opção)
fonte
Acho que o código de teste requer um nível semelhante de engenharia que normalmente seria aplicado ao código de produção. Certamente pode haver argumentos a favor da legibilidade e eu concordaria que isso é importante.
Em minha experiência, entretanto, acho que testes bem fatorados são mais fáceis de ler e entender. Se houver 5 testes em que cada um parece o mesmo, exceto para uma variável que foi alterada e a afirmação no final, pode ser muito difícil encontrar o que é esse único item diferente. Da mesma forma, se for fatorado de forma que apenas a variável que está mudando seja visível e a asserção, será fácil descobrir o que o teste está fazendo imediatamente.
Encontrar o nível certo de abstração durante o teste pode ser difícil e acho que vale a pena fazer.
fonte
Não acho que haja uma relação entre código mais duplicado e legível. Acho que seu código de teste deve ser tão bom quanto o outro código. O código não repetitivo é mais legível do que o código duplicado quando bem feito.
fonte
Idealmente, os testes de unidade não devem mudar muito uma vez que são escritos, então eu tenderia para a legibilidade.
Ter os testes de unidade tão discretos quanto possível também ajuda a manter os testes focados na funcionalidade específica que eles têm como objetivo.
Dito isso, minha tendência é tentar e reutilizar certas partes do código que acabo usando continuamente, como o código de configuração que é exatamente o mesmo em um conjunto de testes.
fonte
"os refatorou para torná-los mais SECOS - a intenção de cada teste não era mais clara"
Parece que você teve problemas para refatorar. Estou apenas supondo, mas se acabou menos claro, isso não significa que você ainda tem mais trabalho a fazer para que você tenha testes razoavelmente elegantes que são perfeitamente claros?
É por isso que os testes são uma subclasse de UnitTest - para que você possa criar bons conjuntos de testes que sejam corretos, fáceis de validar e claros.
Antigamente, tínhamos ferramentas de teste que usavam diferentes linguagens de programação. Era difícil (ou impossível) criar testes agradáveis e fáceis de trabalhar.
Você tem todo o poder de - qualquer linguagem que estiver usando - Python, Java, C # - então use bem essa linguagem. Você pode obter um código de teste de boa aparência, claro e não muito redundante. Não há compensação.
fonte