Eu estava lendo sobre os Unit Tests e queria saber como eles funcionam e se são realmente nescessários, para médias e grandes aplicações ou projetos.
Para sistemas eu nunca vi vantagens em usar teste unitário, no saldo entre prós e contras acaba sendo perda de tempo. Isso é mais requisito de TI para TI, o cliente (atividade fim) quer funcionalidade. Se a equipe consegue entregar demandas com qualidade sem testes de unitários, então não há a menor necessidade. Mas muitos se garantem mais com testes unitários ou desenvolvimento orientado a testes (TDD). Para quem produz e disponibiliza frameworks ou bibliotecas pode ter bom saldo teste unitário, já que os requisitos não são muito dinâmicos.
Teste unitário é uma prática adotada por quase todos os times que produzem código de qualidade. A forma varia, mas o conceito se mantém. Estude, pratique, aprenda e depois decida quando usar e como usar.
-
teste unitário é um dos produtos do desenvolvimento
test-driven
(orientado a testes, ou TDD). Os testes surgem e fluem naturalmente quando se desenvolve orientado a testes, sem necessidade de ficar se matando pra criar teste depois. Entretanto dá sim pra escrever testes unitários depois de implementar, mas é muito mais painful e tendem a ser problemáticos pois a implementação já está feita. A maioria desiste ou não escreve teste unitário pq sempre deixa pra pensar no teste depois de implementar.
Contudo, entre escrever testes depois ou não escrever, menos pior é escrever depois -
e respondendo à sua pergunta, se você quer prezar por código de boa qualidade, fácil manutenção (e refactoring), diminuir incidência de bugs idiotas nas funcionalidades e garantir melhor satisfação do cliente, sim, testes de unidade são altamente necessários.
Na verdade, nenhum teste é necessário. Sempre vão aparecer bugs mesmo, então, para que perder tempo e dinheiro fazendo testes e pagando testers, analistas de testes e afins?
Lendo fica difícil de pegar, mas por favor, me diga que isso foi irônico… kkk
Sim!
Independente da sopa de letrinhas que você escolha (BDD, TDD, DDD, PQP), os testes são parte fundamental. Aliás, não apenas o uso de JUnit e afins é que podem e são considerados testes unitários. Subir a aplicação localmente e executá-la é uma forma, primitiva, de teste unitário. Você está testando a parte mais elementar da aplicação, do caso de uso ou da sprint/história.
Você previne N situações corriqueiras, como erros em labels, quantidade de espaço em campos de texto, erros de tradução, erros de formatação, erros de estilo e muito mais.
Conheço um arquiteto que defende que o tester deve ser melhor remunerado que o programador…
concordo plenamente… Não fazer testes porque sistemas tem BUGs, é o mesmo que resolver não comer já que vai ter fome novamente.
A questão dos testes é que muitos programadores/desenvolvedores pensam que é uma retaliação a si próprio. Quando não.
Eu, por exemplo, cheguei a detestar e alimentar ódio mortal dos testers e achava a criação de testes do JUnit, mockito e etc uma bosta. Mas, com o tempo, percebi que só evoluímos quando vemos nossos erros. Sem contar que boa parte dos erros é falha anterior ao desenvolvimento, pois o levantamento de requisitos foi fraco ou mesmo a especificação técnica é decifitária.
Fail fast. É melhor (mais barato e mais rápido) não deixar passar os erros do que voltar pra corrigir depois. Muitas vezes, quando uma falha é detectada depois que o sistema está concluído, é muito mais difícil alterar um componente sem quebrar outros que o utilizam. Testes unitários servem como método de regressão e facilitam MUITO o trabalho de quem for dar manutenção no código. As vezes um teste end-to-end (ou funcional, ou de aceitação, chame como quiser) demora para ser executado em um sistema grande. Esses testes, na minha opinião (baseado na de Kent Beck, Uncle Bob, Net Pryce/Steve Freeman, Martin Fowler, e outros autores da área), devem guiar o desenvolvimento de testes unitários, sendo o teste maior (o end-to-end) o avaliador do comportamento, e o unitário como avaliador de componentes (ou unidades, como diz o nome).
Discordo de você, mas é só minha opinião (como falei, baseada na experiência e na literatura dos autores que citei antes).
Dessa forma você mata completamente a ideia de TDD. TDD não é o simples ato de criar testes, mas literalmente dirigir o desenvolvimento a partir dos testes. A ideia é que o desenvolvimento de um pequeno componente (uma classe, no caso de OO) comece pelo seu teste. O código de teste age como cliente do componente que vai ser desenvolvido. Dessa forma, o ideal é escrever o teste imaginando que o componente já está funcionando. Podemos escrever a utilização da maneira ideal, com clareza e fluência, sem se preocupar com a implementação. Depois disso, partimos para a implementação para fazer o teste passar. Vamos escrever a utilização do componente (o corpo do teste) sem estar viciado na implementação, pois ainda não o implementamos, coisa que não ocorre quando os testes são escritos depois do componente. A visão de corner cases vai estar um pouco mais clara, o que facilita a captura de erros.
Outra vantagem (na minha opinião, a melhor) de se utilizar TDD é que quando um teste está difícil de ser escrito, há 99% de chance daquele componente ter sido mal pensado, há uma falha de design, pois há mais de uma responsabilidade naquele componente. Assim, fica muito mais fácil (estou falando por experiência, acontece comigo sempre) de detectar e corrigir essas falhas, separando as responsabilidades do componente em múltiplos componentes com uma granularidade um pouco menor, afinal, coerência e coesão são quesitos indispensáveis em código com qualidade.
Se você não consegue suprir as necessidades do componente com mock objects, há algo errado.
Discordo. Testes unitários são testes rápidos e independente de contexto. A ideia é que eles possam ser executados repetidamente pelo desenvolvedor, para capturar erros o mais rápido possível. Testes que dependem de contexto já partem pro lado de testes de integração.
Cito aqui, para concluir, duas passagens do Robert C. Martin:
-
As três leis do Test-Driven Development
- Primeira: É proibido escrever código de produção antes de ter escrito um teste (para esse código) que falhe.
- Segunda: É proibido escrever mais de um teste que falhe por vez.
- Terceira: É proibido escrever mais código de produção do que o necessário para passar no teste escrito
-
Testes devem seguir os princípios F.I.R.S.T:
F: Fast. Testes (principalmente unitários) devem ser rápidos (milissegundos). Isso estimula a execução constante.
I: Independent. Testes não devem depender uns dos outros.
R: Repeatable. Testes devem ser reproduzíveis em qualquer ambiente.
S: Self-Validating. Testes devem ser a validação do código. Eles devem passar ou não, simplesmente assim. Não é legal ter que ficar lendo log pra ver qual teste passou e qual teste falhou através de interpretações.
T: Timely. Eles devem ser escritos antes do código de produção. Dessa forma, você se força a escrever código fácil de testar. Se o teste é escrito antes do código de produção, você pode descobrir depois que escreveu código difícil de ser testado.
Não me expressei adequadamente, quando falei “subir localmente”, quis dizer que abrir a aplicação no computador em que se desenvolve é um tipo de teste unitário.
De qualquer maneira, tudo é sempre muito bonito quando na teoria, o problema é colocar isso em prática, somos (mea culpa também), os ferreiros que usam espetos de pau. Jogamos a culpa (afinal, ela é nossa, fazemos o que bem entendermos com ela) sobre o prazo do projeto, mesmo que quem tenha “definido” o prazo para desenvolvimento (que deveria abranger prazo para testes unitários) sejamos nós.
Não acho que escrever testes unitários atrasam tanto assim o desenvolvimento. Muito pelo contrário, acredito que acelera depois que você pega a prática, pois é muito mais fácil saber o que escrever quando se tem uma situação de erro para corrigir, fica muito mais simples pegar bugs introduzidos com mudanças, detalhes que atrasam o desenvolvimento. Como disse, testes devem ser pequenos, e a ideia é escrever dezenas de testes minúsculos por dia. Do jeito que o gerente briga por prazo mais curto, a gente tem que brigar por mais tempo (se for o caso) para escrever testes. Basta fundamentar que o código vai ficar com mais qualidade, e que vai poupar capital (tanto financeiro quanto humano) no futuro, pois manutenções provavelmente vão ser muito mais ligeiras.
A questão de ter code coverage pra mim é, sinceramente, secundária. Utilizo TDD muito mais pelo fato de pegar falhas de design o mais rápido possível. Hoje, a única situação onde eu não escrevo código fazendo TDD é quando escrevo pequenos snippets aqui pro GUJ. Código fácil de testar é código limpo, seguindo boas práticas de OO. É mais por isso que gosto mesmo.
Sim, cada um com sua opinião, eu nunca precisei usar teste unitário para entregar e manter funcionalidade com qualidade.
Dessa forma você mata completamente a ideia de TDD. TDD não é o simples ato de criar testes, mas literalmente dirigir o desenvolvimento a partir dos testes.
Concordo com tudo o que você citou. Quando eu disse sobre testes “depois”, foi apenas comparando num cenário onde não se escrevem testes de forma alguma. Jamais que escrever testes depois significa TDD.
No mais, tudo o que você falou faz sentido, eu também penso assim. Utilizo TDD pq me preocupo com design e, principalmente, me permite pensar pequeno e de forma incremental. Dessa forma, não me preocupo com a dificuldade de desenvolver algo, pq simplesmente aplicando TDD vou descobrindo e fazendo meu código crescer aos poucos.
Nem eu penso que seja perda de tempo. Acontece que a grande maioria dos desenvolvedores, analistas e até arquitetos que eu conheço ou já trabalhei, simplesmente sacrificam o teste unitário e justificam com esta desculpa estapafúrdia.
Eu ainda penso que seja um problema tão crônico que atinge a formação dos desenvolvedores.
Ensina-se a escrever linhas de código e não a desenvolver em alto nível.
Mas, num país em que a educação é algo terciário…
Professores medíocres formando alunos medíocres. Isso é fato.
E não só isso. “Profissionais” da área que acreditam que isso é normal.
@lvbarbosa de forma geral não abrangendo 100%, noto que o problema que muitas equipes enfrentam ao escrever testes de forma ineficiente (geralmente test-after) ou não escrever testes de forma alguma, além de ser um fator cultural, é também em grande parte por não entenderem COMO escrever testes. Isto dificulta muito a introdução do TDD. Eu já tive dificuldade no passado em aceitar e aplicar TDD, e hoje vejo que na real eu não sabia escrever testes (continuo não sabendo, vamos sempre aprendendo e tentando aperfeiçoar) e, principalmente, não pensava em “como devo testar meu componente” antes mesmo de criá-lo.
Tudo depende de amadurecimento, @leandronsp. E, amadurecimento pode incluir tentativa/erro.
Há ferramentas (plugins e outros) disponíveis para verificar o test coverage e alguns até conseguem analisar a cobertura e profundidade que os testes conseguem atingir.
Eu concordo com a questão cultural da coisa. Mas, ressalto que isso é um problema de base.
Veja quantos tópicos do guj são criados por quem jamais se deu ao trabalho de montar um algoritmo, fazer um teste de mesa e verificar se a lógica empregada é a mais adequada.
Pegue os projetos do github e verifique quantos possuem testes (mesmo porcos) criados.
@Luis_Augusto_Santos, é deprimente olhar o Github. Uma porcentagem tímida de quem publica projetos com testes. Inclusive por vezes, quando alguém de alguma empresa publica vaga (que pede TDD) ou um take-home assignment com README no Github, eu por curiosidade vou lá no perfil da pessoa que postou a vaga e vejo os projetos que têm, e fico pasmo de ver que existem casos onde o owner da vaga pede testes no role description sendo que os próprios projetos dele(a) não têm testes!
Aquela coisa “casa de ferreiro, espeto é de pau!”.
Enfim, o problema vai mesmo na base, eu tenho tentado praticar e explicar o mindset test-driven pra quem tá iniciando. Atualmente estou dando mentoring para um conhecido ingressar no desenvolvimento e qualquer código que fazemos é com teste, inclusive tutoriais de linguagens/frameworks na internet.
Se decide aprender uma nova linguagem/fw utilizando test-driven já ainda no tutorial, o aprendizado fica muito mais eficiente. Pra alguns pode parecer bobeira e overkill, mas dá um boost violento.