Pergunta

Eu arruinei vários testes de unidade há algum tempo, quando passei e os refatorei para torná -los mais SECO-A intenção de cada teste não estava mais clara. Parece que há uma troca entre a legibilidade e a manutenção dos testes. Se eu deixar o código duplicado em testes de unidade, eles serão mais legíveis, mas se eu mudar o SUT, Vou ter que rastrear e alterar cada cópia do código duplicado.

Você concorda que essa compensação existe? Em caso afirmativo, você prefere que seus testes sejam legíveis ou sustentáveis?

Foi útil?

Solução

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, dificulta o refattor do 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 equipamento, considere fazer mais uso do setUp método ou fornecendo mais (ou mais flexível) Métodos de criação.

Se a duplicação estiver no código manipulando o SUT, pergunte-se por que vários testes de "unidade" estão exercendo exatamente a mesma funcionalidade.

Se a duplicação estiver nas afirmações, talvez você precise de alguns Asserções personalizadas. Por exemplo, se vários testes tiverem uma série 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 que você possa escrever assertPersonEqual(Person('Joe', 'Bloggs', 23), person). (Ou talvez você simplesmente precise sobrecarregar o operador de igualdade em Person.)

Como você menciona, é importante que o código de teste seja legível. Em particular, é importante que o intenção de um teste é claro. Acho que, se muitos testes parecerem os mesmos (por exemplo, três quartos das linhas iguais ou praticamente iguais), é difícil identificar e reconhecer as diferenças significativas sem lê-las e compará-las cuidadosamente. Então eu acho que a refatoração para remover a duplicação ajuda A legibilidade, porque todas as linhas de cada método de teste são diretamente relevantes para o objetivo do teste. Isso é muito mais útil para o leitor do que uma combinação aleatória de linhas diretamente relevantes e linhas que são apenas caldeiras.

Dito isto, às vezes os testes exercem situações complexas semelhantes, mas ainda significativamente diferentes, e é difícil encontrar uma boa maneira de reduzir a duplicação. Use o senso comum: se você acha que os testes são legíveis e deixam sua intenção clara, e você se sente confortável em talvez precisar 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 mova -se para algo mais produtivo. Você sempre pode voltar e refatorar os testes mais tarde, quando a inspiração ocorre!

Outras dicas

A legibilidade é mais importante para 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 fortemente fatorados para determinar exatamente o que falhou. Você não deseja que seu código de teste se torne tão complexo que precisa escrever testes de teste de unidade.

No entanto, a eliminação de duplicação geralmente é uma coisa boa, desde que não obscureça nada, e a eliminação da duplicação em seus testes pode levar a uma API melhor. Apenas certifique -se de não passar pelo ponto de diminuição de retornos.

O código e os testes de implementação são animais diferentes e as regras de fatoração se aplicam de maneira diferente a eles.

O código ou estrutura duplicada é sempre um cheiro no código de implementação. Quando você começa a ter o Boilerplate na implementação, você 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:

  • Mantendo os testes dissociados. O acoplamento excessivo de teste pode dificultar a alteração de um único teste de falha que precisa ser atualizado porque o contrato mudou.
  • Manter os testes significativos isoladamente. Quando um único teste está falhando, deve ser razoavelmente simples descobrir exatamente o que está testando.

Costumo ignorar a duplicação trivial no código de teste, desde que cada método de teste permaneça mais curto que cerca de 20 linhas. Eu gosto quando o ritmo de configuração-verificação é aparente nos métodos de teste.

Quando a duplicação se aproxima da parte "verificar" dos testes, geralmente é benéfico definir métodos de afirmação personalizados. Obviamente, esses métodos ainda devem testar uma relação claramente identificada que possa ser aparente no nome do método: assertPegFitsInHole -> bom, assertPegIsGood -> ruim.

Quando os métodos de teste crescem longos e repetitivos, às vezes acho útil definir modelos de teste de preenchimento em blindagem que recebem alguns parâmetros. Em seguida, os métodos de teste reais são reduzidos a uma chamada para o método de modelo com os parâmetros apropriados.

Quanto a muitas coisas em programação e teste, não há resposta clara. Você precisa desenvolver um gosto, e a melhor maneira de fazer isso é cometer erros.

Você pode reduzir a repetição usando vários sabores diferentes de Métodos de utilidade 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 ele. Quando você muda o design de uma classe e precisa voltar e ajustar 10 métodos de teste diferentes que fazem as mesmas etapas de configuração, é frustrante.

Concordo. O comércio existe, mas é diferente em lugares diferentes.

É mais provável que refatorie o código duplicado para a criação de estado. Mas menos probabilidade de refatorar a parte do teste que realmente exerce o código. Dito isto, se o exercício do código sempre leva várias linhas de código, acho que isso é um cheiro e refatorar o código real em teste. E isso melhorará a legibilidade e a manutenção do código e dos testes.

Jay Fields cunhou a frase de que "as DSLs deveriam estar úmidas, não secas", onde ÚMIDO significa frases descritivas e significativas. Eu acho que o mesmo se aplica a 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 de revelação de intenções. Se, por exemplo, você especificar o mesmo recurso de vários ângulos diferentes, é esperado uma certa quantidade de duplicação.

Eu amo o RSPEC por causa disso:

Tem 2 coisas para ajudar -

  • Grupos de exemplo compartilhados para testar comportamento comum.
    Você pode definir um conjunto de testes e, em seguida, 'incluir' que definir seus testes reais.

  • contextos aninhados.
    Você pode ter um método de 'configuração' e 'desmontagem' para um subconjunto específico de seus testes, não apenas todos da classe.

Quanto mais cedo isso .NET/Java/Outras estruturas de teste adotarem esses métodos, melhor (ou você pode usar o IronRuby ou Jruby para escrever seus testes, o que eu pessoalmente acho que é a melhor opção)

Eu não acho que exista uma relação entre código mais duplicado e legível. Eu acho que seu código de teste deve ser tão bom quanto seu outro código. O código não repetido é mais legível do que o código duplicado quando bem feito.

Idealmente, os testes de unidade não devem mudar muito assim que estiverem escritos, então eu me inclinaria para a legibilidade.

Ter testes de unidade é o mais discreto possível também ajuda a manter os testes focados na funcionalidade específica que eles estão direcionando.

Com isso dito, tendem a tentar reutilizar certas peças de código que acabo usando repetidamente, como o código de configuração exatamente o mesmo em um conjunto de testes.

Sinto 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 feitos em favor da legibilidade e eu concordo que isso é importante.

Na minha experiência, no entanto, acho que os testes bem-fortos são mais fáceis de ler e entender. Se houver 5 testes de que cada um parece o mesmo, exceto uma variável que mudou 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 modo que apenas a variável que está mudando é visível e a afirmação, é fácil descobrir o que o teste está fazendo imediatamente.

Encontrar o nível certo de abstração quando os testes podem ser difíceis e sinto que vale a pena fazer.

"Refatorou-os para torná-los mais secos-a intenção de cada teste não estava mais clara"

Parece que você teve problemas para fazer a refatoração. Estou apenas adivinhando, mas se isso acabou menos claro, isso não significa que você ainda tem mais trabalho a fazer para fazer testes razoavelmente elegantes que são perfeitamente claros?

É por isso que os testes são uma subclasse do UNITTEST - para que você possa projetar boas suítes de teste corretas, fáceis de validar e limpar.

Nos tempos antigos, tínhamos ferramentas de teste que usavam diferentes linguagens de programação. Era difícil (ou impossível) projetar agradável, fácil de trabalhar com testes.

Você tem todo o poder - qualquer que esteja usando - python, java, c# - então use bem esse idioma. Você pode obter um código de teste bonito que é claro e não muito redundante. Não há troca.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top