Quando é apropriado não testar a unidade?

130

Eu trabalho em uma pequena empresa como desenvolvedor solo. Eu sou o único desenvolvedor na empresa, de fato. Eu tenho vários projetos (relativamente) grandes que eu escrevi e mantenho regularmente, e nenhum deles tem testes para apoiá-los. Quando inicio novos projetos, muitas vezes me pergunto se devo tentar uma abordagem TDD. Parece uma boa ideia, mas honestamente nunca posso justificar o trabalho extra envolvido.

Eu trabalho duro para ter uma visão de futuro no meu design. Percebo que, certamente, um dia, outro desenvolvedor terá que manter meu código ou, pelo menos, solucioná-lo. Mantenho as coisas da maneira mais simples possível e comento e documente coisas que seriam difíceis de entender. E o fato é que esses projetos não são tão grandes ou complicados que um desenvolvedor decente teria dificuldade em compreendê-los.

Muitos dos exemplos que vi de testes são detalhados, cobrindo todas as facetas do código. Como sou o único desenvolvedor e estou muito próximo do código em todo o projeto, é muito mais eficiente seguir um padrão de gravação e teste manual. Também acho que os requisitos e os recursos mudam com frequência suficiente para que a manutenção dos testes possa adicionar uma quantidade considerável de arrasto em um projeto. Tempo que poderia ser gasto resolvendo as necessidades do negócio.

Então eu acabo com a mesma conclusão a cada vez. O retorno do investimento é muito baixo.

De vez em quando, configuro alguns testes para garantir que escrevi um algoritmo corretamente, como calcular o número de anos que alguém esteve na empresa com base na data de contratação. Mas do ponto de vista de cobertura de código, eu cobri cerca de 1% do meu código.

Na minha situação, você ainda acharia uma maneira de tornar o teste de unidade uma prática regular, ou estou justificado em evitar essa sobrecarga?

UPDATE: Algumas coisas sobre a minha situação que deixei de fora: Meus projetos são todos aplicativos da web. Para cobrir todo o meu código, eu teria que usar testes de interface do usuário automatizados, e essa é uma área em que ainda não vejo um grande benefício em relação ao teste manual.

    
por Ken Pespisa 08.04.2011 / 17:41
fonte

14 respostas

80

A lot of the examples I've seen of tests get down to the minutiae, covering all facets of the code.

Então? Você não precisa testar tudo . Apenas as coisas relevantes.

Since I'm the only developer and I'm very close to the code in the entire project, it is much more efficient to follow a write-then-manually-test pattern.

Isso é realmente falso. Não é mais eficiente. É realmente apenas um hábito.

O que outros desenvolvedores solo fazem é escrever um esboço ou esboço, escrever os casos de teste e depois preencher o esboço com o código final.

Isso é muito, muito eficiente.

I also find requirements and features change frequently enough that maintaining tests would add a considerable amount of drag on a project.

Isso é falso também. Os testes não são o arrasto. As mudanças nos requisitos são o arrasto.

Você precisa corrigir os testes para refletir os requisitos. Se suas minúcias, ou de alto nível; escrito primeiro ou escrito por último.

O código não é feito até que os testes sejam aprovados. Essa é a única verdade universal do software.

Você pode ter um teste de aceitação "aqui está".

Ou você pode ter alguns testes de unidade.

Ou você pode ter os dois.

Mas não importa o que você faça, sempre haverá um teste para demonstrar que o software funciona.

Eu sugeriria que um pouco de formalidade e um bom conjunto de ferramentas de testes unitários tornam esse teste muito mais útil.

    
por 08.04.2011 / 18:06
fonte
102

Imagine que você tinha um conjunto de testes que poderiam ser executados em um link de olho e acendessem uma luz verde ou vermelha. Imagine que este conjunto de testes testou tudo ! Imagine que tudo o que você precisava fazer para executar o conjunto de testes era digitar ^ T. Que poder isso lhe daria?

Você poderia fazer uma alteração no código sem medo de quebrar alguma coisa? Você poderia adicionar um novo recurso sem medo de quebrar um recurso antigo? Você poderia limpar o código confuso rapidamente, sem medo de causar danos?

Sim, você pode fazer todas essas coisas! E o que aconteceria com o seu código ao longo do tempo? Ficaria mais limpo e limpo porque não haveria risco de limpá-lo.

Vamos imaginar que você tenha uma pequena fada no seu ombro. Toda vez que você escrevia uma linha de código, a fada adicionava algo à suíte de testes que testava que aquela linha de código fazia o que pretendia fazer. Assim, a cada dois segundos, você pode clicar em ^ T e ver que a última linha de código que você escreveu funcionou.

Quanta depuração você acha que faria?

Se isso soa como fantasia, você está certo. Mas a realidade não é muito diferente. Substitua o link do olho por alguns segundos, e a fada com a disciplina TDD, e você conseguiu muito bem.

Digamos que você esteja voltando para um sistema criado há um ano e tenha esquecido como criar um dos objetos centrais. Existem testes que criam esse objeto de todas as maneiras que podem ser criados. Você pode ler esses testes e movimentar sua memória. Precisa chamar uma API? Existem testes que chamam essa API de todas as formas possíveis. Esses testes são pequenos documentos , escritos em uma linguagem que você entende. Eles são completamente inequívocos. Eles são tão formais que eles executam. E eles não podem ficar fora de sincronia com o aplicativo!

Não vale a pena o investimento? Só podes estar a brincar comigo! Como alguém poderia querer esse conjunto de testes? Faça um favor a si mesmo e pare de brincar com bobagens. Aprenda a fazer bem o TDD e veja quanto tempo você passa e quanto mais limpo é o seu código.

    
por 09.04.2011 / 01:04
fonte
32

O erro que você está cometendo é que você está vendo o teste como um investimento de tempo sem retorno imediato. Não necessariamente funciona assim.

Primeiramente, os testes de escrita realmente concentram-se no que esta parte do seu código precisa fazer.

Em segundo lugar, a execução deles revela erros que, de outra forma, surgiriam nos testes.

Em terceiro lugar, às vezes, eles exibem bugs que de outra forma não apareceriam nos testes e, em seguida, realmente o incomodariam na produção.

Em quarto lugar, se você encontrar um bug com um sistema em execução e criar um teste de unidade para ele, não poderá reintroduzir esse bug mais tarde. Isso pode ser uma grande ajuda. Bugs reintroduzidos são comuns e muito irritantes.

Em quinto lugar, se você precisar passar o código para outra pessoa, uma suíte de testes tornará sua vida muito mais fácil. Além disso, se você ignorar um projeto e voltar a ele depois de alguns anos, não estará mais tão perto dele e também será útil para você.

Minha experiência consistentemente tem sido através do desenvolvimento de um projeto, tendo testes unitários decentes sempre tornou o processo mais rápido e confiável.

    
por 08.04.2011 / 17:58
fonte
31

Os caras do JUnit (framework de teste de unidade Java) têm uma filosofia que se for simples demais para testar, não teste ele . É altamente recomendável ler as Perguntas frequentes sobre práticas recomendadas , pois é bastante pragmático.

O TDD é um processo diferente de escrever seu software. A premissa básica por trás do teste de unidade é que você gastará menos tempo no depurador percorrendo o código e mais rapidamente descobrirá se a alteração do seu código acidentalmente quebra alguma outra coisa no sistema. Isso se encaixa com o TDD. O ciclo TDD é assim:

  1. Escreva um teste
  2. Assista a falha (prove que você tem algo a fazer)
  3. Escreva exatamente o que é necessário para fazer o teste passar - não mais.
  4. Assista passar (yay!)
  5. Refatorar (melhorar)
  6. Lave, enxague e repita

O que é menos óbvio sobre a aplicação do TDD é que ele muda a forma como o seu código de escrita . Forçando-se a pensar em como testar / validar se o código está funcionando, você está escrevendo um código testável. E já que estamos falando de testes de unidade, isso geralmente significa que seu código se torna mais modular. Para mim, o código modular e testável é uma grande vitória na frente.

Agora, você precisa testar coisas como as propriedades do C #? Imagine uma propriedade definida assim:

bool IsWorthTesting {get; set;}

A resposta seria "não" não vale a pena ser testada, porque neste momento você está testando o recurso de idioma. Apenas confie que os caras da plataforma C # acertaram. Além disso, se falhar, o que poderia você fazer para corrigir isso?

Além disso, você descobrirá que há certas partes do seu código que muito bem serão muito trabalhosas para serem testadas adequadamente. Isso significa que não faça isso, mas certifique-se de testar o código que usa / é usado pelo problema complicado:

  • Exceções verificadas que só podem ocorrer se uma instalação foi mal. Java tem uma tonelada desses. Você é obrigado a escrever um bloco catch ou declarar a exceção verificada, mesmo que não haja como falhar, sem invadir os arquivos instalados.
  • Interfaces do usuário. Encontrar o controle em teste e invocar os eventos certos para simular as ações de um usuário são muito problemáticos e, em alguns casos, impossíveis. No entanto, se você usar o padrão Modelo / Visualização / Controlador, poderá certificar-se de que o modelo e os controladores sejam testados e deixar a parte da vista para testes manuais.
  • Interações entre cliente e servidor. Este não é mais um teste de unidade e agora é um teste integração . Escreva todas as partes que vão até o envio e recebimento de mensagens pelo fio, mas não passe pelo fio. Uma boa abordagem é reduzir a responsabilidade do código que realmente fala sobre o fio para as comunicações brutas. Em seu código de teste de unidade, ignore o objeto de comunicação para garantir que os serviços estão se comportando conforme o esperado.

Acredite ou não, o TDD irá ajudá-lo a cair em um ritmo sustentável de desenvolvimento. Não é por causa da magia, mas sim porque você tem um loop de feedback apertado e você é capaz de pegar erros realmente idiotas rapidamente. O custo de consertar esses erros é essencialmente constante (pelo menos o suficiente para propósitos de planejamento), porque os pequenos erros nunca crescem como grandes erros. Compare isso com a natureza em rajadas dos sprints de limpeza de depuração / depuração de código.

    
por 08.04.2011 / 18:44
fonte
23

Você precisa equilibrar o custo do teste com o custo dos bugs.

Escrever um teste de unidade de 10 linhas para uma função que abre um arquivo, onde a falha é "arquivo não encontrado" é inútil.

Uma função que faz algo complexo para uma estrutura de dados complexa - então, obviamente, sim.

O bit complicado está no meio. Mas lembre-se que o valor real dos testes unitários não está testando a função específica, está testando as interações difíceis entre eles. Portanto, um teste de unidade que detecta que uma alteração em um bit de código interrompe alguma função em um módulo diferente a 1000 linhas de distância, vale seu peso no café.

    
por 08.04.2011 / 17:54
fonte
22

O teste é o jogo.

Criar um teste é uma aposta de que o custo de erros em uma unidade ocorrendo e de não capturá-los com esse teste (agora e durante todas as futuras revisões de código) é maior que o custo de desenvolvimento do teste. Esses custos de desenvolvimento de testes incluem coisas como folha de pagamento para engenharia de teste, tempo de colocação no mercado, custos de oportunidade perdidos por não codificar outras coisas e etc.

Como qualquer aposta, às vezes você ganha, às vezes você perde.

Algum tempo atrasado software com muito menos bugs ganha sobre coisas rápidas, mas com bugs que chega ao mercado primeiro. Às vezes o oposto. Você precisa analisar as estatísticas em seu campo específico e o quanto a gerência deseja jogar.

É improvável que alguns tipos de bugs sejam feitos, ou que sejam eliminados de qualquer teste inicial de sanidade, já que, estatisticamente, não vale a pena criar testes específicos adicionais. Mas às vezes o custo de um bug é tão grande (médico, nuclear etc.) que uma empresa precisa fazer uma aposta perdida (semelhante à compra de seguro). Muitos aplicativos não têm um custo de falha tão alto e, portanto, não precisam de uma cobertura de seguro mais econômica. Outros fazem.

    
por 26.04.2011 / 21:30
fonte
10

Meu conselho é testar somente o código que você deseja que funcione corretamente.

Não teste o código que deseja causar bugs e cause problemas para você no futuro.

    
por 09.04.2011 / 07:45
fonte
8

I often wonder if I should try a TDD approach. It sounds like a good idea, but I honestly can never justify the extra work involved.

TDD e Testes Unitários não são a mesma coisa.

Você pode escrever código e adicionar testes de unidade posteriormente. Isso não é TDD e é muito trabalho extra.

TDD é a prática de codificação em um loop de luz vermelha. Luz verde. Refatorar iterações.

Isso significa escrever testes para códigos que ainda não existem, observar os testes falharem, corrigir o código para fazer os testes funcionarem e, em seguida, tornar o código "correto". Isso geralmente evita que você trabalhe

Uma das vantagens do TDD é que ele reduz a necessidade de pensar em trivialidades. Coisas como erros off-by-one desaparecem. Você não precisa procurar documentação da API para descobrir se a lista retornada começa em 0 ou 1, apenas faça isso.

    
por 08.04.2011 / 18:08
fonte
3

Eu trabalhei em um sistema onde testamos quase tudo. As execuções notáveis nos testes foram o código de saída PDF e XLS.

Por quê? Fomos capazes de testar as partes que reuniram os dados e construímos o modelo que foi usado para criar a saída. Também pudemos testar as partes que descobriram quais partes do modelo iriam para os arquivos PDF. Não fomos capazes de testar se o PDF parecia ok porque isso era totalmente subjetivo. Não foi possível testar se todas as partes em um PDF eram legíveis para um usuário típico porque isso também era subjetivo. Ou se a escolha entre os gráficos de barras e pizza estivesse correta para o conjunto de dados.

Se a saída for subjetiva, há pouco teste de unidade que você pode fazer valer o esforço.

    
por 08.04.2011 / 22:39
fonte
2

Para muitas coisas, um 'teste de escrita, em seguida, manualmente' não leva mais tempo do que escrever alguns testes. A economia de tempo vem da capacidade de executar novamente esses testes a qualquer momento.

Pense nisso: Se você tem uma cobertura de recurso decente com seus testes (não confundir com cobertura de código), e digamos que você tenha 10 recursos - clicar em um botão significa que você tem aproximadamente 10 yous refazer seus testes ... enquanto você senta e bebe seu café.

Você também não tem para testar a minutae. Você pode escrever testes de integração que cubram os seus recursos se você não quiser obter os detalhes básicos ... IMO, alguns testes de unidade são muito refinados testando o idioma e a plataforma, e não o código.

TL; DR Realmente nunca é apropriado porque os benefícios são bons demais.

    
por 08.04.2011 / 17:52
fonte
2

Duas respostas muito boas que encontrei estão aqui:

  1. Quando testar unidade versus teste manual
  2. O que não testar quando se trata de unidade Teste?

As justificativas para evitar a sobrecarga percebida:

  • Tempo / economia imediata para sua empresa
  • Possível economia de tempo / custo na solução de problemas / manutenção / extensão a longo prazo, mesmo depois de você ter ido embora.

Você não gostaria de deixar um ótimo produto do seu lado como a prova de qualidade do seu trabalho? Falando em termos egoístas, não é melhor para você fazer isso?

    
por 08.04.2011 / 17:53
fonte
1

Os desenvolvedores profissionais gravam testes de unidade porque, a longo prazo, economizam tempo. Você vai testar seu código mais cedo ou mais tarde, e se você não fizer isso, e se você tiver que consertar bugs mais tarde, eles serão mais difíceis de consertar e causar mais efeitos.

Se você está escrevendo código sem testes e não tem bugs, então tudo bem. Eu não acredito que você possa escrever um sistema não-trivial com zero bugs, então eu suponho que você está testando de uma forma ou de outra.

Testes de unidade também são cruciais para impedir regressões quando você modifica ou refaz código mais antigo. Eles não provam que sua mudança não quebrou o código antigo, mas dão muita confiança (contanto que eles passem, é claro :))

Eu não voltaria e escreveria um lote inteiro de testes para código que você já tenha enviado, mas da próxima vez que precisar modificar um recurso, sugiro tentar escrever testes para esse módulo ou classe, aumente sua cobertura para 70% + antes de aplicar qualquer alteração. Veja se isso ajuda você.

Se você tentar e pode dizer honestamente que não foi de ajuda, então é justo, mas eu acho que há evidências suficientes na indústria que eles ajudam a fazer com que valha a pena testar sua abordagem.

    
por 08.04.2011 / 18:00
fonte
1

Parece que a maioria das respostas são pro-TDD, mesmo que a pergunta não seja sobre TDD, mas sobre testes unitários em geral.

Não existe uma regra completamente objetiva para o teste de unidade ou não para o teste de unidade. Mas há um par de vezes em que parece que muitos programadores não fazem teste unitário:

  1. Métodos privados

Dependendo da sua filosofia OOP, você pode criar métodos privados para dissociar rotinas complexas de seus métodos públicos. Os métodos públicos geralmente são planejados para serem chamados em muitos lugares diferentes e usados com frequência, e os métodos privados são realmente chamados por um ou dois métodos públicos em uma classe ou módulo para algo muito específico. Geralmente, é suficiente escrever testes de unidade para métodos públicos, mas não os métodos privados subjacentes que fazem parte da mágica acontecer. Se algo der errado com um método privado, seus testes de unidade de método público deverão ser bons o suficiente para detectar esses problemas.

  1. Coisas que você já conhece devem funcionar (ou coisas testadas por outra pessoa)

Muitos programadores novos vão contra isso quando estão aprendendo a testar e acham que precisam testar cada linha que está sendo executada. Se você estiver usando uma biblioteca externa e sua funcionalidade for bem testada e documentada por seus autores, geralmente é inútil testar a funcionalidade específica em testes de unidade. Por exemplo, alguém pode escrever um teste para certificar-se de que seu modelo ActiveRecord persiste o valor correto para um atributo com um retorno de chamada "before_save" para o banco de dados, mesmo que esse comportamento em si seja já completamente testado no Rails. O método (s) que o retorno de chamada está chamando, talvez, mas não o próprio comportamento de retorno de chamada. Quaisquer problemas subjacentes com bibliotecas importadas seriam melhor revelados através de testes de aceitação, em vez de testes de unidade.

Ambos podem ser aplicados se você está fazendo TDD ou não.

    
por 04.03.2016 / 23:53
fonte
0

Ken, Eu e muitos outros desenvolvedores lá fora chegamos à mesma conclusão que você várias vezes ao longo de nossas carreiras.

A verdade que acredito que você encontrará (assim como muitos outros) é que o investimento inicial de testes de escrita para seu aplicativo pode parecer assustador, mas, se bem escrito e direcionado às partes corretas de seu código, eles podem realmente poupe uma tonelada de tempo.

Meu grande problema foi com as estruturas de teste disponíveis. Eu nunca realmente senti que eles eram o que eu estava procurando, então eu apenas rolei minha própria solução muito simples. Isso realmente ajudou a me levar ao "lado negro" do teste de regressão. Compartilharei um pseudo-fragmento básico do que fiz aqui e espero que você encontre uma solução que funcione para você.

public interface ITest {
    public string Name {
        get;
    }
    public string Description {
        get;
    }
    public List<ITest> SubTests {
        get;
    }
    public TestResult Execute();
}

public class TestResult {
    public bool Succesful {
        get;
        set;
    }

    public string ResultMessage {
        get;
        set;
    }

    private Dictionary<ITest, TestResult> subTestResults = new Dictionary<ITest, TestResult>();
    public Dictionary<ITest, TestResult> SubTestResults {
        get {
            return subTestResults;
        }
        set {
            subTestResults = value;
        }
    }
}

A única parte complicada depois disso é descobrir qual nível de granularidade você acha que é o melhor "estrondo para o dólar" para qualquer projeto que você esteja fazendo.

A criação de um catálogo de endereços exigirá, obviamente, menos testes do que um mecanismo de pesquisa empresarial, mas os fundamentos realmente não mudam.

Boa sorte!

    
por 08.04.2011 / 18:53
fonte