Se o seu código de teste de unidade “cheira” isso realmente importa?

52

Normalmente eu apenas jogo meus testes unitários usando copiar e colar e todo tipo de outras práticas ruins. Os testes de unidade geralmente acabam parecendo muito feios, cheios de "cheiro de código", mas isso realmente importa? Eu sempre digo a mim mesmo, desde que o código "real" seja "bom", é tudo o que importa. Além disso, o teste unitário geralmente requer vários "hacks fedorentos", como funções de stub.

Qual é o nível de preocupação com testes unitários mal projetados ("fedorentos")?

    
por Buttons840 18.05.2011 / 21:41
fonte

12 respostas

72

O teste unitário é importante? Sim definitivamente. No entanto, eles são diferentes dos cheiros de código porque os testes de unidade servem a um propósito diferente e têm um conjunto diferente de tensões que informam seu design. Muitos cheiros no código não se aplicam aos testes. Dada a minha mentalidade de TDD, eu diria que os cheiros de teste de unidade são mais importantes que os cheiros de código porque o código está lá apenas para satisfazer os testes.

Aqui estão alguns cheiros comuns para testes unitários:

  • Fragilidade : seus testes falham com frequência e inesperadamente, mesmo para alterações de código aparentemente triviais ou não relacionadas?
  • State Leak : seus testes falham diferentemente dependendo, por exemplo, de quais ordens eles são executados?
  • Setup / Teardown Bloat : Seus blocos de configuração / desmontagem são longos e estão crescendo mais? Eles realizam algum tipo de lógica de negócios?
  • Tempo de execução lento : Seus testes demoram muito para serem executados? Algum dos seus testes individuais de unidade demora mais de um décimo de segundo para ser executado? (Sim, estou falando sério, um décimo de segundo).
  • Atrito : Os testes existentes dificultam a criação de novos testes? Você se encontra lutando com falhas de teste frequentemente durante a refatoração?

A importância dos cheiros é que eles são indicadores úteis de design ou outras questões mais fundamentais, ou seja, "onde há fumaça, há fogo". Não procure apenas cheiros de teste, procure também a causa subjacente.

Aqui, por outro lado, existem algumas boas práticas para testes de unidade:

  • Feedback rápido e focado : Seus testes devem isolar a falha rapidamente e fornecer informações úteis sobre sua causa.
  • Minimize a distância do código de teste : deve haver um caminho claro e curto entre o teste e o código que o implementa. Longas distâncias criam loops de feedback desnecessariamente longos.
  • Teste uma coisa de cada vez : Testes de unidade só devem testar uma coisa. Se você precisar testar outra coisa, escreva outro teste.
  • Um erro é um teste que você esqueceu de escrever : O que você pode aprender com essa falha ao escrever testes melhores e mais completos no futuro?
por 18.05.2011 / 22:27
fonte
67

Fique preocupado. Você escreve testes de unidade para provar que seu código age da maneira esperada. Eles permitem que você refatore rapidamente com confiança. Se seus testes forem frágeis, difíceis de entender ou se forem difíceis de manter, você irá ignorar os testes fracassados ou desativá-los à medida que sua base de código evoluir, negando muitos dos benefícios de escrever testes em primeiro lugar.

    
por 18.05.2011 / 21:48
fonte
19

Acabei de ler A arte do teste unitário há alguns dias. O autor defende o máximo de cuidado em seus testes de unidade, assim como o seu código de produção.

Eu experimentei testes mal-escritos e insustentáveis em primeira mão. Eu escrevi alguns dos meus próprios. É virtualmente garantido que, se um teste for uma tarefa difícil de manter, não será mantido. Uma vez que os testes estão fora de sincronia com o código em teste, eles se tornam ninhos de mentiras e enganos. O objetivo dos testes unitários é instilar confiança de que não quebramos nada (isto é, eles criam confiança). Se os testes não são confiáveis, eles são mais do que inúteis.

    
por 18.05.2011 / 22:05
fonte
7

Contanto que seu teste de unidade realmente teste seu código na maioria dos casos. (Disse mais de propósito, pois é difícil, por vezes, encontrar todos os resultados possíveis). Eu acho que o código "fedorento" é sua preferência pessoal. Eu sempre escrevo código de uma maneira que eu possa ler e entender em poucos segundos, em vez de vasculhar o lixo e tentar entender o que é o quê. Especialmente quando você volta depois de uma quantidade significativa de tempo.

Linha de fundo - seu teste, suponha ser fácil. Você não quer se confundir com código "fedorento".

    
por 18.05.2011 / 21:45
fonte
6

Definitivamente. Algumas pessoas dizem "qualquer teste é melhor que nenhum teste". Eu discordo totalmente - testes mal escritos atrapalham seu tempo de desenvolvimento, e você acaba desperdiçando dias corrigindo testes "quebrados" porque eles não eram bons testes de unidade em primeiro lugar. Para mim, no momento, as duas coisas em que estou me concentrando para tornar meus testes valiosos, em vez de um fardo, são:

Manutenção

Você deve testar o resultado ( o que acontece), não o método ( como acontece). Sua configuração para o teste deve ser tão dissociada da implementação quanto possível: somente configurar resultados para chamadas de serviço, etc., que são absolutamente necessários.

  • Use uma estrutura de simulação para garantir que seus testes não dependam de nada externo
  • Favor stubs over mocks (se sua estrutura diferenciar entre eles) sempre que possível
  • Nenhuma lógica nos testes! Ifs, switches, for-eaches, casos, try-catches etc. são todos grandes, pois podem introduzir erros no próprio código de teste

Legibilidade

Não há problema em permitir um pouco mais de repetição em seus testes, o que você normalmente não permitiria em seu código de produção, se isso os tornasse mais legíveis. Basta equilibrar isso com o material de manutenção acima. Seja explícito no que o teste está fazendo!

  • Tente manter um estilo "organizar, agir, afirmar" para seus testes. Isso separa sua configuração e as expectativas do cenário, da ação que está sendo executada e do resultado sendo afirmado.
  • Mantenha uma asserção lógica por teste (se o nome do seu teste tiver "e" nele, talvez seja necessário dividi-lo em vários testes)

Em conclusão, você deve estar muito preocupado com os testes "malcheirosos" - eles podem acabar sendo apenas um desperdício de tempo, sem fornecer nenhum valor.

Você disse:

Unit testing usually requires various "smelly hacks" like stubbing functions.

Parece que você poderia fazer com a leitura de algumas técnicas de Testes Unitários, como o uso de um framework Mocking, para tornar sua vida muito mais fácil. Eu recomendo strongmente que A Arte de Testes Unitários , que cobre os tópicos acima e muito mais. Eu achei interessante depois de ter lutado com testes mal-escritos, insustentáveis, "fedorentos" por um longo tempo. É um dos melhores investimentos de tempo que fiz este ano!

    
por 19.05.2011 / 02:42
fonte
5

Duas perguntas para você:

  • Você tem certeza absoluta de que está testando o que pensa que está testando?
  • Se alguém mais olhar para o teste de unidade, ele será capaz de descobrir o que o código deve fazer ?

Existem maneiras de lidar com tarefas repetitivas em seus testes de unidade, que são mais comumente configurados e desmontados. Essencialmente, você tem um método de configuração de teste e um método de teste - todos os frameworks de teste de unidade têm suporte para isso.

Os testes unitários devem ser pequenos e de fácil compreensão. Se eles não estiverem e o teste falhar, como você resolverá o problema em um período de tempo razoavelmente curto. Facilite a si mesmo por alguns meses quando você tiver que voltar ao código.

    
por 18.05.2011 / 21:49
fonte
5

Quando o lixo começar a cheirar, é hora de retirá-lo. Seu código de teste deve ser tão limpo quanto seu código de produção. Você mostraria para sua mãe?

    
por 18.05.2011 / 22:54
fonte
3

Além das outras respostas aqui.

O código de baixa qualidade nos testes de unidade não está restrito ao seu pacote de testes unitários.

Uma das funções preenchidas pelo Teste de Unidade é a Documentação.

O pacote Unit Test é um dos lugares em que se procura descobrir como uma API deve ser usada.

Não é improvável que os chamadores da sua API copiem partes do seu conjunto de testes de unidade, o que leva ao código do seu conjunto de testes defeituoso, que infecta o código ativo em outro lugar.

    
por 19.05.2011 / 11:54
fonte
3

Quase não enviei minha resposta, pois demorei um pouco para descobrir como abordar isso como uma questão legítima.

As razões pelas quais os programadores se envolvem em "melhores práticas" não são por razões estéticas, ou porque querem código "perfeito". É porque economiza tempo.

  • Economiza tempo porque um código bom é mais fácil de ler e entender.
  • Economiza tempo porque, quando você precisa encontrar um bug para corrigir, é mais fácil e rápido localizá-lo.
  • Economiza tempo porque, quando você deseja estender o código, é mais fácil e rápido fazer isso.

Então, a pergunta que você está fazendo (do meu ponto de vista) é que devo economizar tempo ou escrever códigos que desperdiçam meu tempo?

Para essa pergunta, posso dizer apenas quão importante é o seu tempo?

FYI: stubbing, mocking, e monkey patching todos têm usos legítimos. Eles só "cheiram" quando seu uso não é apropriado.

    
por 18.05.2011 / 22:26
fonte
2

Eu tento especificamente não tornar meus testes de unidade muito robustos. Eu tenho visto testes de unidade que começam a lidar com erros em nome de ser robusto. O que você acaba com testes que engolem os erros que eles estão tentando pegar. Os testes de unidade também têm que fazer algumas coisas divertidas para fazer as coisas funcionarem. Pense em toda a idéia de acessadores privados usando a reflexão ... se eu visse um monte deles em todo o código de produção, eu ficaria preocupado 9 vezes em cada 10. Acho que mais tempo deveria ser gasto em pensar sobre o que está sendo testado , em vez de limpeza do código. Os testes precisarão ser alterados com muita frequência, de qualquer maneira, se você fizer alguma refatoração importante, então por que não apenas hacká-los juntos, ter menos sentimentos de propriedade e estar mais motivados para reescrevê-los ou reformulá-los quando chegar a hora?

    
por 18.05.2011 / 22:49
fonte
2

Se você estiver indo para uma cobertura pesada e de baixo nível, você gastará tanto tempo ou mais no código de teste quanto no código do produto ao fazer qualquer modificação séria.

Dependendo da complexidade da configuração do teste, o código pode ser mais complexo. (httpcontext.current vs. o monstro horrível de tentar construir um falso com precisão, por exemplo)

A menos que seu produto seja algo em que você raramente faça uma alteração nas interfaces existentes e suas entradas de nível de unidade sejam muito simples de configurar, eu ficaria PELO MENOS preocupado com o entendimento dos testes como o produto real.

    
por 18.05.2011 / 23:23
fonte
0

O Code Smells é apenas um problema no código que precisa ser alterado ou entendido em algum momento no futuro.

Então, eu acho que você deve consertar os códigos cheiros quando você tem que ir "perto" para o teste unitário dado, mas não antes.

    
por 23.06.2011 / 15:37
fonte