paint-brush
Por que não acho que o TDD seja essencialby@shai.almog
1,162
1,162

Por que não acho que o TDD seja essencial

Shai Almog5m2022/11/22
Read on Terminal Reader

Test Driven Development coloca ênfase na unidade sobre os testes de integração. O resultado pode ser uma qualidade inferior com bugs embutidos no produto.
featured image - Por que não acho que o TDD seja essencial
Shai Almog HackerNoon profile picture


Recentemente, dei uma palestra sobre depuração para a London Java Community. Durante a parte de perguntas e respostas da palestra, alguém me perguntou sobre minha abordagem para Test Driven Development. No passado, eu olhava para essa prática de uma forma mais positiva. Escrever muitos testes. Como aquilo pode ser mau?

Mas com o passar do tempo, vejo isso sob uma luz diferente.


Eu o vejo como uma ferramenta muito limitada que tem casos de uso muito específicos. Não se encaixa no tipo de projetos que construo e muitas vezes atrapalha os processos fluidos que deveria promover. Mas vamos voltar atrás por um segundo. Gostei muito desse post que separa os tipos e problemas em TDD. Mas vamos simplificar um pouco, vamos esclarecer que todo PR deve ter uma boa cobertura. Isso não é TDD. É apenas uma boa programação.


TDD é mais do que isso. Nela, precisamos definir as restrições e então resolver o problema. Essa abordagem é superior a resolver o problema e, em seguida, verificar se as restrições estão corretas? Essa é a premissa principal do TDD versus apenas escrever uma boa cobertura de teste.

O bom

TDD é uma abordagem interessante. É especialmente útil ao trabalhar com linguagens de tipo flexível. Nessas situações, o TDD é maravilhoso, pois preenche o papel de um compilador e linter estrito.

Há outros casos em que faz sentido. Quando estamos construindo um sistema que tem entradas e saídas bem definidas. Já me deparei com muitos desses casos ao criar cursos e materiais. Ao trabalhar com dados do mundo real, isso às vezes acontece quando temos um middleware que processa dados e os gera em um formato predefinido.


A ideia é construir a equação com as variáveis ocultas no meio. Em seguida, a codificação torna-se o preenchimento da equação. É muito conveniente em casos como esse. A codificação torna-se o preenchimento dos espaços em branco.

O mal

“Desenvolvimento Orientado a Testes É Contabilidade de Entrada Dupla. Mesma disciplina. Mesmo raciocínio. Mesmo resultado.” – Tio Bob Martin


Eu diria que o teste é um pouco como a contabilidade de partidas dobradas. Sim. Deveríamos ter testes. A questão é devemos construir nosso código com base em nossos testes ou vice-versa? Aqui a resposta não é tão simples.


Se tivermos um sistema pré-existente com testes, então o TDD faz todo o sentido do mundo. Mas testando um sistema que ainda não foi construído. Há alguns casos em que faz sentido, mas não com tanta frequência quanto se poderia pensar.


A grande reivindicação do TDD é “seu design”. Os testes são efetivamente o design do sistema e, em seguida, implementamos esse design. O problema com isso é que também não podemos depurar um design. No passado, trabalhei em um projeto para uma grande empresa japonesa. Esta empresa tinha um dos maiores e mais detalhados conjuntos de livros de design de anexos. Com base nessas especificações de projeto, a empresa construiu milhares de testes. Deveríamos passar por uma grande quantidade de testes com nosso sistema. Observe que a maioria nem era automática.


Os testes tinham bugs. Houve muitas implementações concorrentes, mas nenhuma delas encontrou os bugs nos testes. Por quê? Todos eles usaram o mesmo código-fonte de implementação de referência. Fomos a primeira equipe a pular isso e fazer uma implementação de sala limpa. Isso perpetuou esses bugs no código, alguns deles eram sérios bugs de desempenho que afetaram todas as versões anteriores.

Mas o verdadeiro problema era o progresso lento. A empresa não poderia avançar rapidamente. Os proponentes do TDD serão rápidos em comentar que um projeto TDD é mais fácil de refatorar, pois os testes nos dão uma garantia de que não teremos regressões. Mas isso se aplica a projetos com testes realizados após o fato.

O pior

O TDD se concentra fortemente em testes de unidade rápidos. É impraticável executar testes de integração lentos ou testes de execução longa que podem ser executados durante a noite em um sistema TDD. Como você verifica a escala e a integração em um sistema principal?


Em um mundo ideal, tudo se encaixará como legos. Eu não vivo em um mundo assim, os testes de integração falham muito. Essas são as piores falhas com os bugs mais difíceis de rastrear. Eu prefiro ter uma falha nos testes de unidade, é por isso que os tenho. Eles são fáceis de corrigir. Mas mesmo com cobertura perfeita, eles não testam a interconexão adequadamente. Precisamos de testes de integração e eles encontram os bugs mais terríveis.


Como resultado, o TDD enfatiza demais os testes de unidade “bom ter” sobre os testes de integração essenciais. Sim, você deve ter os dois. Mas devo fazer os testes de integração. Esses não se encaixam tão bem no processo TDD.

Teste orientado para a direita

Escrevo testando da maneira que escolho caso a caso. Se eu tiver um caso em que testar com antecedência é natural, usarei isso. Mas, na maioria dos casos, escrever o código primeiro parece mais natural para mim. Revisar os números de cobertura é muito útil ao escrever testes e isso é algo que faço após o fato.


Como mencionei antes, só verifico a cobertura para testes de integração. Gosto de testes de unidade e de monitorar a cobertura lá, pois também quero uma boa cobertura. Mas, para qualidade, apenas os testes de integração importam. Um PR precisa de testes de unidade, não me importo se os escrevemos antes da implementação. Devemos julgar os resultados.

Automação Ruim

Quando a Tesla estava construindo suas fábricas do Modelo 3, ela entrou no inferno da produção. A fonte dos problemas foi a tentativa de automatizar tudo. O Princípio de Pareto se aplica perfeitamente à automação. Algumas coisas são muito resistentes à automação e tornam todo o processo muito pior.


Um ponto em que isso realmente falha é no teste de interface do usuário. Soluções como Selenium etc. fizeram grandes progressos nos testes de front-ends da web. Ainda assim, a complexidade é tremenda e os testes são muito frágeis. Acabamos com testes difíceis de manter. Pior, achamos a interface do usuário mais difícil de refatorar porque não queremos reescrever os testes.


Provavelmente podemos cruzar 80% da funcionalidade testada, mas há um ponto de retornos decrescentes para automação. Nesses ambientes, o TDD é problemático. A funcionalidade é fácil, mas construir os testes torna-se insustentável.

Finalmente

Não sou contra TDD mas não recomendo e efetivamente não uso. Quando faz sentido começar com um teste, posso fazer isso, mas isso não é realmente TDD. Eu julgo o código com base nos resultados. O TDD pode fornecer ótimos resultados, mas geralmente enfatiza demais os testes de unidade. Os testes de integração são mais importantes para a qualidade a longo prazo.


A automação é ótima. Até que pare. Há um ponto em que os testes automatizados fazem pouco sentido. Isso nos pouparia muito tempo e esforço para aceitar isso e focar nossos esforços em uma direção produtiva.


Isso é do meu viés como desenvolvedor Java que gosta de linguagens estritas e seguras. Linguagens como JavaScript e Python podem se beneficiar de um volume maior de testes devido à sua flexibilidade. Portanto, o TDD faz mais sentido nesses ambientes.


Em resumo, o teste é bom. Porém, TDD não faz testes melhores. É uma abordagem interessante se funcionar para você. Em alguns casos é enorme. Mas a ideia de que o TDD é essencial ou mesmo que irá melhorar significativamente o código resultante não faz sentido.




Também publicado aqui.