Hoje, conversei com meu colega sobre testes de unidade. A coisa toda começou quando ele me perguntou "ei, onde estão os testes para essa aula, eu vejo apenas um?". A classe inteira era um gerente (ou um serviço, se você preferir chamar assim) e quase todos os métodos estavam simplesmente delegando coisas a um DAO, por isso era semelhante a:
SomeClass getSomething(parameters) {
return myDao.findSomethingBySomething(parameters);
}
Um tipo de clichê sem lógica (ou pelo menos não considero uma delegação tão simples como lógica), mas um clichê útil na maioria dos casos (separação de camadas etc.). E tivemos uma discussão bastante demorada sobre se eu deveria testá-lo ou não na unidade (acho que vale a pena mencionar que fiz totalmente o DAO). Seus principais argumentos são que não era TDD (obviamente) e que alguém pode querer fazer o teste para verificar o que esse método faz (não sei como isso pode ser mais óbvio) ou que no futuro alguém possa querer alterar o implementação e adicione uma nova (ou mais parecida com "qualquer") lógica a ela (nesse caso, acho que alguém deveria simplesmente testar essa lógica ).
Isso me fez pensar, no entanto. Devemos nos esforçar para obter a maior cobertura de teste em%? Ou então é simplesmente uma arte pelo bem da arte? Simplesmente não vejo razão para testar coisas como:
- getters e setters (a menos que eles realmente tenham alguma lógica neles)
- código "clichê"
Obviamente, um teste para esse método (com zombaria) levaria menos de um minuto, mas acho que ainda é tempo perdido e um milissegundo a mais para cada IC.
Existem razões racionais / não "inflamáveis" pelas quais alguém deve testar todas as linhas de código (ou tantas quanto puder)?
fonte
Respostas:
Eu segui a regra de ouro de Kent Beck:
Teste tudo o que poderia quebrar.
Claro, isso é subjetivo até certo ponto. Para mim, getters / setters triviais e one-liners como o seu acima geralmente não valem a pena. Mas, novamente, passo a maior parte do tempo escrevendo testes de unidade para código legado, apenas sonhando com um bom projeto greenfield TDD ... Em tais projetos, as regras são diferentes. Com o código legado, o objetivo principal é cobrir o máximo de terreno com o mínimo de esforço possível, de modo que os testes de unidade tendem a ser de nível mais alto e mais complexo, mais parecidos com testes de integração, se alguém é pedante quanto à terminologia. E quando você está lutando para obter uma cobertura geral de código de 0%, ou apenas consegue aumentar mais de 25%, os getters e setters de teste de unidade são a menor das suas preocupações.
OTOH em um projeto TDD greenfield, pode ser mais prático escrever testes mesmo para esses métodos. Especialmente porque você já escreveu o teste antes de ter a chance de começar a se perguntar "essa linha vale um teste dedicado?". E pelo menos esses testes são triviais para escrever e rápidos para executar, por isso não é grande coisa de qualquer maneira.
fonte
Existem alguns tipos de teste de unidade:
Se você escrevesse seu teste primeiro, isso faria mais sentido - como seria de esperar chamar uma camada de acesso a dados. O teste falharia inicialmente. Você escreveria o código de produção para fazer o teste passar.
Idealmente, você deve testar o código lógico, mas as interações (objetos chamando outros objetos) são igualmente importantes. No seu caso, eu faria
Atualmente, não há lógica, mas nem sempre será o caso.
No entanto, se você tiver certeza de que não haverá lógica nesse método e provavelmente permanecerá o mesmo, consideraria chamar a camada de acesso a dados diretamente do consumidor. Eu faria isso apenas se o restante da equipe estivesse na mesma página. Você não deseja enviar uma mensagem errada para a equipe dizendo "Ei pessoal, tudo bem ignorar a camada de domínio, basta ligar diretamente para a camada de acesso a dados".
Eu também me concentraria em testar outros componentes se houvesse um teste de integração para esse método. Ainda estou para ver uma empresa com sólidos testes de integração.
Dito tudo isso - eu não testaria cegamente tudo. Eu estabeleceria os pontos de acesso (componentes com alta complexidade e alto risco de quebra). Eu então me concentraria nesses componentes. Não faz sentido ter uma base de código em que 90% da base de código é bastante direta e é coberta por testes de unidade, quando 10% restantes representam a lógica principal do sistema e não são cobertos por testes de unidade devido à sua complexidade.
Finalmente, qual é o benefício de testar esse método? Quais são as implicações se isso não funcionar? Eles são catastróficos? Não se esforce para obter alta cobertura de código. A cobertura do código deve ser um subproduto de um bom conjunto de testes de unidade. Por exemplo, você pode escrever um teste que irá percorrer a árvore e fornecer 100% de cobertura desse método, ou você pode escrever três testes de unidade que também fornecerão 100% de cobertura. A diferença é que, ao escrever três testes, você testa casos extremos, em vez de simplesmente andar na árvore.
fonte
Aqui está uma boa maneira de pensar sobre a qualidade do seu software:
Para clichês e funções triviais, você pode contar com a verificação de tipo para realizar seu trabalho e, para o resto, precisa de casos de teste.
fonte
Na minha opinião, a complexidade ciclomática é um parâmetro. Se um método não for suficientemente complexo (como getters e setters). Nenhum teste de unidade é necessário. O nível de complexidade ciclomática de McCabe deve ser maior que 1. Outra palavra deve ter no mínimo 1 bloco.
fonte
Um SIM retumbante com TDD (e com algumas exceções)
Tudo bem controverso, mas eu diria que quem responde 'não' a essa pergunta está perdendo um conceito fundamental de TDD.
Para mim, a resposta é um retumbante sim se você seguir o TDD. Se você não é, então não é uma resposta plausível.
O DDD no TDD
O TDD é frequentemente citado como tendo os principais benefícios.
Separe a responsabilidade da implementação
Como programadores, é terrivelmente tentador pensar nos atributos como algo de significância e getters e setter como uma espécie de sobrecarga.
Mas os atributos são um detalhe de implementação, enquanto setters e getters são a interface contratual que realmente faz os programas funcionarem.
É muito mais importante escrever que um objeto deve:
e
então como esse estado é realmente armazenado (para o qual um atributo é o mais comum, mas não o único).
Um teste como
é importante para a parte da documentação do TDD.
O fato de a eventual implementação ser trivial (atributo) e não trazer benefícios de defesa deve ser desconhecido para você quando você escreve o teste.
A falta de engenharia de ida e volta ...
Um dos principais problemas no mundo do desenvolvimento de sistemas é a falta de engenharia de ida e volta 1 - o processo de desenvolvimento de um sistema é fragmentado em subprocessos desconexos cujos artefatos (documentação, código) geralmente são inconsistentes.
1 Brodie, Michael L. "John Mylopoulos: costurando sementes da modelagem conceitual". Modelagem Conceitual: Fundamentos e Aplicações. Springer Berlin Heidelberg, 2009. 1-9.
... e como o TDD resolve
É a parte da documentação do TDD que garante que as especificações do sistema e seu código sejam sempre consistentes.
Projete primeiro, implemente depois
No TDD, escrevemos primeiro o teste de aceitação com falha, e depois escrevemos o código que os deixou passar.
Dentro do BDD de nível superior, escrevemos os cenários primeiro e depois os fazemos passar.
Por que você deve excluir setters e getter?
Em teoria, é perfeitamente possível no TDD uma pessoa escrever o teste e outra implementar o código que o faz passar.
Então pergunte a si mesmo:
Como getters e setters são uma interface pública para uma classe, a resposta é obviamente sim , ou não haverá como definir ou consultar o estado de um objeto.
Obviamente, se você escrever o código primeiro, a resposta pode não ser tão clara.
Exceções
Existem algumas exceções óbvias a essa regra - funções que são detalhes claros da implementação e claramente não fazem parte do design do sistema.
Por exemplo, um método local 'B ()':
Ou a função privada
square()
aqui:Ou qualquer outra função que não faça parte de uma
public
interface que precise de ortografia no design do componente do sistema.fonte
Quando se deparar com uma questão filosófica, volte aos requisitos de direção.
Seu objetivo é produzir software razoavelmente confiável a um custo competitivo?
Ou é para produzir software da mais alta confiabilidade possível quase independentemente do custo?
Até certo ponto, os dois objetivos de qualidade e velocidade / custo de desenvolvimento se alinham: você gasta menos tempo escrevendo testes do que corrigindo defeitos.
Mas além desse ponto, eles não. Não é tão difícil conseguir, digamos, um bug relatado por desenvolvedor por mês. Reduzir para metade para um a cada dois meses libera apenas um orçamento de talvez um dia ou dois, e muitos testes extras provavelmente não reduzirão pela metade a taxa de defeitos. Portanto, não é mais uma simples vitória / vitória; você precisa justificá-lo com base no custo do defeito para o cliente.
Esse custo variará (e, se você quiser ser mau, a capacidade deles de aplicar esses custos de volta a você, seja através do mercado ou de uma ação judicial). Você não quer ser mau, então conta esses custos na íntegra; Às vezes, alguns testes ainda globalmente tornam o mundo mais pobre por sua existência.
Em resumo, se você tentar aplicar cegamente os mesmos padrões em um site interno que o software de voo de um avião de passageiros, você acabará fora do negócio ou na cadeia.
fonte
Sua resposta depende da sua filosofia (acredite que é Chicago vs Londres? Tenho certeza que alguém vai procurar). O júri ainda não se pronunciou sobre a abordagem mais econômica em termos de tempo (porque, afinal, esse é o maior fator que leva a dedicar menos tempo a correções).
Algumas abordagens dizem testar apenas a interface pública, outras dizem testar a ordem de cada chamada de função em todas as funções. Muitas guerras santas foram travadas. Meu conselho é tentar as duas abordagens. Escolha uma unidade de código e faça-a como X e outra como Y. Depois de alguns meses de teste e integração, volte e veja qual deles atende melhor às suas necessidades.
fonte
É uma pergunta complicada.
A rigor, eu diria que não é necessário. É melhor escrever testes de nível de sistema e unidade de estilo BDD para garantir que os requisitos de negócios funcionem conforme o esperado em cenários positivos e negativos.
Dito isto, se o seu método não for coberto por esses casos de teste, você deverá questionar por que ele existe em primeiro lugar e se é necessário ou se existem requisitos ocultos no código que não são refletidos na documentação ou nas histórias de usuários que deve ser codificado em um caso de teste no estilo BDD.
Pessoalmente, gosto de manter a cobertura por linha em torno de 85 a 95% e realizar check-ins para garantir que a cobertura de teste de unidade existente por linha atinja esse nível em todos os arquivos de código e que nenhum arquivo seja descoberto.
Supondo que as melhores práticas de teste estejam sendo seguidas, isso oferece muita cobertura, sem forçar os desenvolvedores a perder tempo tentando descobrir como obter cobertura adicional em códigos difíceis de exercer ou triviais, simplesmente por uma questão de cobertura.
fonte
O problema é a questão em si, você não precisa testar todos os "métodos" ou todas as "classes" necessárias para testar todos os recursos de seus sistemas.
É o pensamento-chave em termos de características / comportamentos, em vez de pensar em termos de métodos e classes. É claro que existe um método aqui para fornecer suporte a um ou mais recursos; no final, todo o seu código é testado, pelo menos todo o código é importante na sua base de códigos.
No seu cenário, provavelmente essa classe "manager" é redundante ou desnecessária (como todas as classes com um nome que contém a palavra "manager"), ou talvez não, mas parece um detalhe de implementação, provavelmente essa classe não merece uma unidade teste porque esta classe não possui nenhuma lógica comercial relevante. Provavelmente, você precisa dessa classe para que algum recurso funcione; o teste para esse recurso abrange essa classe; dessa forma, você pode refatorar essa classe e fazer um teste para verificar se o que importa, seus recursos, ainda funciona após o refator.
Pense em recursos / comportamentos que não estão nas classes de método, não posso repetir isso o suficiente.
fonte
Sim, idealmente 100%, mas algumas coisas não podem ser testadas por unidade.
Getters / Setters são estúpidos - apenas não os use. Em vez disso, coloque sua variável de membro na seção pública.
Obtenha código comum e teste-o por unidade. Isso deve ser tão simples quanto isso.
Ao não fazer isso, você pode perder alguns erros muito óbvios. Os testes de unidade são como uma rede segura para detectar certos tipos de bugs, e você deve usá-la o máximo possível.
E a última coisa: estou em um projeto em que as pessoas não querem perder tempo escrevendo testes de unidade para algum "código simples", mas depois decidiram não escrever. No final, partes do código se transformaram em uma grande bola de lama .
fonte