Testes de Unidade são realmente nescessários?

73 respostas Resolvido
junitjava
M

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.

73 Respostas

J

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.

E

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.

L
  1. 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 :smiley:

  2. 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.

L

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?

I

Lendo fica difícil de pegar, mas por favor, me diga que isso foi irônico… kkk

L

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…

I

concordo plenamente… Não fazer testes porque sistemas tem BUGs, é o mesmo que resolver não comer já que vai ter fome novamente.

L

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.

L
Solucao aceita

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:

  1. As três leis do Test-Driven Development

    1. Primeira: É proibido escrever código de produção antes de ter escrito um teste (para esse código) que falhe.
    2. Segunda: É proibido escrever mais de um teste que falhe por vez.
    3. Terceira: É proibido escrever mais código de produção do que o necessário para passar no teste escrito
  2. 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.

L

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.

L

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.

J

Sim, cada um com sua opinião, eu nunca precisei usar teste unitário para entregar e manter funcionalidade com qualidade.

L

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.

L

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…

L

Professores medíocres formando alunos medíocres. Isso é fato.

L

E não só isso. “Profissionais” da área que acreditam que isso é normal.

L

@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.

L

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.

L

@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.

L

@leandronsp, os problemas do mundo corporativo sempre vão girar em torno do que eu chamo de fator lucro: se está dando lucro, não mexe.
Se a empresa possui um departamento de desenvolvimento, alguém ganha dinheiro, tem lucro, com retrabalho.
Se a empresa terceiriza isso, tem alguém que também lucra com isso.
Logo, qual o interesse em fazer código bom? Coeso, testado e de fácil manutenção? O pessoal da empresa onde trabalho chama isso de empregabilidade. Você desenvolve, cria problemas e garante que vai ter vaga para corrigir o problema que não existiria se você programasse direito.

L

@Luis_Augusto_Santos quanto ao fator lucro, isso que fazem beira a burrice, pq dá sim pra ter lucro com coisa bem feita. Basta o “manager” que tá encarregado saber buscar informação e aplicar o que precisa ser feito pra aumentar o lucro.

Se nem o “manager” é bem preparado, quem dirá o “developer” que ele contratar. Aí já começa a entrar em outro assunto, mas o foco do problema está mesmo onde você destacou acima: falta de treinamento e conhecimento, e isso começa de cima.


Presenciei um caso de um conhecido onde uma consultoria XXX (multinacional renomada com XX anos de história e bla bla bla) contratou um desenvolvedor Java, mas como ele não tinha os skills necessários pra atender a demanda do cliente, ao invés de treiná-lo adequadamente, colocaram-no pra RECRUTAR outro desenvolvedor Java com skills próprios para aquele cliente em específico. Nonsense total.

K

Testes são fundamentais.

Mas para complementar o já falado, testes unitários não são o único tipo que existe. Ao realizar uma entrega ao cliente por exemplo, o que importa é ter testes de aceitação. Ao colocar várias partes de um sistema para trabalhar em conjunto é interessante ter testes de integração. Da mesma forma que fazer manualmente testes é também importante dependendo da aplicação, e pode ser a única forma de identificar alguns defeitos.

Testes unitários são interessantes mas nem tudo é suscetível a ser testado assim de uma forma boa, mas para CRUDs, e sistemas simples pode ser aplicado sem problemas.

Mas, na minha opinião, se vai escolher entre fazer unit tests e testes de integração, se for pra ser um ou outro, é melhor codificar os testes de integração do que unitários, devido a que assim você vai poder avaliar se a funcionalidade inteira está OK com todas as partes funcionando em conjunto, não apenas um pedacinho isolado que é o que o teste unitário avalia. Por isso acho bobagem só focar em testes unitários, e esquecer dos vários outros tipos que existem.

J

Concordo, para o cliente o que importa são os testes de aceitação da funcionalidade que de fato será utilizada, e não os meios (isso é opção de TI). Onde trabalho os analistas de qualidade tem como opção de ferramenta o Selenium. A previsibilidade da aceitação é natural pela presença do cliente, não é mundo isolado de TI como fábrica de software (ai sim doutrinas e diversas siglas podem ser necessárias).

K

Bom mas mesmo que fosse um mundo isolado de TI, onde você vende uma biblioteca para 3os usarem, seu teste de aceitação pode ser simplesmente uma versão de um teste de integração. E o importante sempre é isso, garantir que os requisitos do cliente são atendidos, e isso é feito com aceitação.

É bom como você falou manter a perspectiva que testes unitário serve para outras coisas: é um auxilio ao desenvolvimento. Serve para identificar especificamente quais métodos estão com problemas, sendo que num teste de integração você não tem essa granularidade. Com isso você consegue testar pre e pos condições, coisa que pode ser feita automaticamente algumas linguagens que possibilitam, e dá até pra emular usando o sistema de tipos, mas não é comum ver normalmente. E pra finalizar outra utilidade é no caso de linguagens com características de tipagem dinamica (incluindo não só linguagens totalmente dinâmicas mas também onde há queries SQL no codigo e frameworks Java que façam uso pesado de reflection) para garantir que o código “compila” como deveria, substituindo no caso um papel que poderia ser feito por um compilador melhor ou SGBD.

J

Exatamente. Como falei no primeiro post, principalmente para quem disponibiliza frameworks e bibliotecas isso tudo pode ser positivo, ai tenho que concordar. Para atender a “atividade fim” o que importa pro cliente é garantir os requisitos finais para o seu negócio, com testes funcionais. Durante o protótipo e desenvolvimento é importante constante feedback natural com o cliente, para melhor previsibilidade de aceitação do resultado final. Os meios técnicos ficam como opção da TI, se a equipe precisa seguir mais alguma diretriz técnica para conseguir manter bem o sistema.

P

Se o processo é realmente iterativo nao deve fazer diferença se os testes são escritos antes ou depois. O benefício para o código vai ser o mesmo quando se está constantemente redesenhando o código e eliminando débito técnico.

Aliás, o processo que você descreveu como TDD, parece muito a experiência de desenvolver numa linguagem com REPL.

P

É normal pedir aval do gerente pra esse tipo de coisa?

Na minha época gerente lidava com preservação da condição das instalações físicas, auxiliava caso precisasse trocar de monitor, cadeira, essas coisas.

I

Nao li a thread toda, mas por um momento fiquei com medo. Achei que todos estavam falando que testes unitários não era necessários. Depois vi que alguns estavam brincando.

Não consigo ver projetos sem testes unitários. É uma loucura um projeto não ter testes automatizados. Nenhum programador deve, ou deveria, confiar no seu código, por mais que o cara seja bom. Quantas vezes voce nao alterou aquele código e falou, agora vai, e não foi? rs…

Testes de unidade e de integração juntos dá uma segurança tremenda, e não é uma segurança apenas enganadora, quando se escreve bons testes. Ou seja, projeto sem testes, é um projeto falho, na minha opinião.

Escrever um bom código, pensar numa boa arquitetura, não leva bem mais tempo? Náo seria mais fácil fazer algo mais gambiarra para entregar o projeto pro cliente logo? Afinal, pro cliente final, tanto faz se o código tá limpo ou não, o que importa é funcionar. Sim, um bom código, leva bem mais tempo, porém evita diversas manutenções futuras e as vezes até a necessidade de jogar o projeto no lixo e reescrever do zero. O mesmo é pra criar testes. Leva mais tempo, porém você está fazendo aquilo, a fim de garantir saúde para seu projeto.

L

Concordo, mas quando foi falado acima da importância de ter testes unitários, isto não quer dizer que NÃO é pra termos outros tipos de testes. Teste unitário não substitui nenhum tipo de teste, e nenhum outro tipo de teste substitui unitário.

Isto só se aplica pra quem não faz TDD. Pra quem aplica test-driven (BDD + TDD + [qualquer coisa]DD), é comum começar top-down, ou seja, no final da entrega você tem testes de UI (aceitação), de integração e os unitários.

Sim, você tem razão. Por isso que quem faz as coisas test-driven não cai nesse problema, pois escreve todo e qualquer tipo de teste sem dor.

K

Beleza, da pra usar TDD sim.

Mas como falei nem tudo da pra desenvolver com TDD. Por exemplo, imagina um caso de uso num jogo onde o usuário faz clique em um objeto na tela, e o visual dele muda para ter um highlight neon ao redor. Fora isso o importante é ter o sistema testado, não é ter usado TDD ou não.

L

De fato, projetos sem teste algum geralmente têm tendência a serem caros. Quando não vão à falência total, perduram no mercado durante anos, sendo caros como são, sangrando a conta bancária de alguém que acredita que está economizando dinheiro.

E complementando: projeto que só tem testes de integração ou os testes mais “high-level” (e.g Selenium) também tende a ter problemas de manutenção. Mesmo que as features sejam consistentes de uma ponta à outra, no médio e longo prazo esses testes passam a ser cada vez mais caros. Claro que, pra quem gosta de pagar caro, isto não é um problema pq sempre tem quem goste de jogar tempo e dinheiro fora :stuck_out_tongue:

Reiterando, de forma geral:

  • Teste unitário não exime necessidade de outros testes (integração, UI, manual, etc)
  • Outros tipos de testes não eximem necessidade de testes unitários
  • BDD e TDD são baratos, pois evita a necessidade de manter um esquadrão gigante de developers e testers pra manter a consistência do produto
L

Sim, nem tudo dá pra desenvolver com TDD pq TDD faz parte do workflow test-driven. Mais importante do que usar o termo BDD e/ou TDD, vejo que é importante ter o mindset test-driven, pois desta forma resolve o teu problema em questão: com BDD você especifica que o visual deve mudar quando o usuario faz clique no objeto; pode-se escrever cenarios no Cucumber (como exemplo), obviamente os cenarios vao falhar e daí em diante você continua aplicando BDD/TDD para os componentes; com isso, naturalmente vão surgir testes de integração e unitários, e no final de tudo quando teu cenário passar, a funcionalidade está pronta.

Geralmente confundimos BDD com TDD e vice-versa. Pra mim é tudo um só: test-driven, não importa o termo. Sempre top-down, orientado a funcionalidade e também testabilidade/design/integração dos componentes necessários pra feature.

K

Você respondeu meu “como faz?” dizendo “depois de ter elaborado, é só implementar”. Está pre-supondo que é possível elaborar um teste razoável, que foi justamente o que eu perguntei como faria. Sinceramente eu não vejo como, pois tem coisas que são subjetivas, da pra implementar o glow de diferentes jeitos com efeitos que são difíceis de quantificar qual é melhor ou pior, exceto claro o caso em que dá um erro de compilação rodar o mesmo!

Bom, só perguntei porque você disse que dava pra fazer testes unitários em tudo, e eu sinceramente não acho que seja o caso. No caso que citei pode ser testado sim mas observando “manualmente” com o sistema rodando, mas não vejo como testar algo que depende da experiência do usuário de forma automatizada.

Isso inclui outros requisitos de um sistema como implementação de bibliotecas de UI, a própria UX do sistema tem vários fatores difíceis de quantificar, e coisas que dependam do “feeling”. Você pode tentar ignorar esses aspectos de um sistema, mas teste automatizado em geral, mesmo sendo algo util, está longe de ser uma panaceia. E por isso TDD e BDD são metodologias que se não cumprem tudo o que prometem, justamente por prometerem resolverem tudo usando os testes automatizados.

PS: Desculpa, deu tilt aqui e editei o post antigo sem querer, em lugar de criar outro respondendo! rs

L

Dado que o componente ainda não existe, e que pra quem faz test-driven já tem o teste de aceitação e integração falhando, o teste unitário consiste em garantir que o componente chama outro componente (ou lib externa) que faz o efeito. No teu caso específico é um teste de mock (nem sempre é assim), e uma vez que esse teste passa e o lib/componente externo também tem testes, os testes de integração e aceitação também vão passar.

Enfim, não vou tentar te convencer a nada. Só expus meu ponto na thread através de experiências e literaturas. Cabe a cada um de nós absorver o que for útil (ou não).

L

Acho que faz diferneça sim, pelo simples fato de que a gente pode viciar no que foi implementado e não enxergar caminhos de teste não pensados na hora da implementação.

Uma situação análoga é quando passamos um tempão escrevendo, por exemplo, um artigo. No final, podemos achar que ele está impecável, mas com certeza alguém diferente (e não viciado no texto, como nós estamos) vai encontrar erros/inconsistências. Escrever o teste antes seria uma espécie de simulação dessa terceira pessoa, já que ainda não implementamos a funcionalidade.

Bem, pelo menos é isso que os caras mais experientes da comunidade falam (e eu concordo).

Na minha situação sim, o manager está por dentro de todo o desenvolvimento (ele é formado em computação) e opina sobre as decisões tomadas, além de participar sempre dos encontros onde as discussões técnicas ocorrem. Ele que guia o fluxo do projeto, junto com outros caras mais técnicos.

P

ok, mas não existe terceira pessoa. Por que assumir que a mesma pessoa viciada numa determinada implementação vai ter um lampejo de criatividade assim que começar a escrever os testes primeiro, e começar a testar todos os casos e situações possíveis?

L

Claro que existe, o ambiente de produção com milhares de usuários pondo tudo à prova simultaneamente.

Não acho que vai fazer tudo 100%, todo mundo erra. Contudo, com certeza a pessoa vai pensar em mais situações além daquelas que implementou, quando se trata de um componente mais complexo do que um get/set, com alguns possíveis fluxos de execução.

L

Sinceramente, vocês acham mesmo que vale a pena discutir este tipo de coisa?
Vamos começar lá pelo começo. Quantos aqui seguem processos? Muitos dirão que processos não servem para nada. Outros que são fundamentais.
E quem já trabalhou com bancos de dados (relacionais) normalizados (de verdade)? Aposto que, ao menos uma vez na vida, vocês tiveram que dar uma de DBA ou, pior, viram o DBA fazer coisas que, do ponto de vista da normalização, são verdadeiras aberrações. Em prol do quê? Cada um irá defender seu lado.
Especificação? Quantos trabalham em empresas onde um sistema é 100% especificado (ou uma sprint é 100% especificada) e onde essa especificação é feita com qualidade? E quantos sistemas vocês mesmos foram os analistas, desenvolvedores e testadores?
Boas práticas são fundamentais. Ponto.
Quem está acostumado ao XGH (extreme go horse) dificilmente enxergará as benesses de um scrum bem implementado ou de um BDD.
Mercadologicamente falando, de 100 sistemas rodando por aí, quantos vocês acham que foram feitos seguindo as boas práticas? Quantos são bem testados?

L

Por que não? O @pfk66 parece ser um cara muito experiente, eu gosto muito dessas discussões que temos as vezes, os pontos dele sempre me fazem parar para pensar e refazer meu pensamento.

Não é porque todo mundo faz errado que eu vou fazer também. Não me forço a fazer as coisas certas, faço porque gosto mesmo hehe

L

É uma discussão no estilo mac x windows ou linux x windows.
Você faz por achar certo. Eu faço, a empresa me obriga. Outros não fazem por não serem solicitados e assim vai.
Agora, se formos discutir como seria a melhor forma de implementar um modelo de testes em um ambiente corporativo que peca em todos os aspectos, aí sim, eu acho produtivo.

A

Acho que para discutir profissionalismo de alguém baseado no uso ou nao de testes, esse argumento deveria ser baseado em fatos(pesquisas realizadas com projetos reais) e nao em opiniao, experiência pessoal ou apelo a autoridade.

Eu gosto de testes unitários (e de linguagens estáticas), pois sinto que os dois me dao mais segurança. Porém isso é uma opiniao que nao vou impor como verdade a todos, e nem dizer que o mercado é ruim por falta de um ou outro.

R

Então pessoal, vou deixar meu feedback sobre testes.
Tenho 12 anos de experiencia em desenvolvimento, trabalho com várias linguagens.
Quando conheci os testes de unidades, testes e integração e tudo mais, acreditava que isso se travada uma tremenda baboseira e não dei importância.
Algum tempo depois, me deparei com uma questão em que, o sistema ficou tão grande que, era alterar alguma coisa e, com certeza alguma outra quebraria. Era certeza isso!
Então, comecei a escrever testes para pegar esses problemas e, vi que os testes me ajudaram a garantir a qualidade do sistema. Garantir que, ao colocar alguma coisa em produção, os testes me davam um feedback e segurança de dizer com peito estufado “o sistema ta no ar”.
Mesmo em sistemas pequenos é primordial fazer testes. Digo isso pois, sistemas pequenos podem crescer e se tornar um frankenstein sem controle.
Em resumo: testes darão certeza de que o software desenvolvido estará funcional da forma como é esperado.
Ferramentas
Uso algumas ferramentas. No Eclipse uso Coverage para obter feedback de quanto o código está coberto por testes.
Também uso um plugin Coverage no Jenkins para dar um feedback para equipe de quanto o código está coberto por testes.
E, ainda é possível escrever métricas para impedir o build de um código caso ele não atenda uma métrica estabelecida. Ex.: posso dizer ao Jenkins que invalide uma build caso o código esteja com menos de 80% de cobertura de testes.
Tenho um lema: não abra mão da qualidade

P

O ambiente de produção existe independente se a pessoa escreve teste primeiro ou depois.

Headbanging-drive design?

A questão não é quantas soluções o programador é capaz de pensar, mas qual que efetivamente separa a parte stateful (não-testavel) da parte stateless (testável).

L

@romero.dias, muito bonito teu testemunho.
Quando entrei na empresa onde hoje trabalho, há 3 anos atrás, me disseram que um dos pilares era qualidade. Que se abria mão de tudo, exceto da qualidade.
Três anos depois e eu digo que ninguém nunca prezou pela qualidade aqui. Ou o conceito de qualidade que aqui se tem é: funcionou, tá valendo.
Como o pessoal tem falado aí, muito mais de cada um do que “requisito de mercado”. Difícil o gestor que tenha maturidade para entender quão necessário é perder um tempo para garantir a qualidade (e garantir que os testes são efetivos).

P

Em ambientes corporativos é mais comum o teste do sistema completo, geralmente feito por humanos.

Com relação a testes unitários, pra que facilitar o entendimento do sistema pra quem esta chegando agora de fora? :wink:

L

Isso se encaixa em documentação, também.

R

@Luis_Augusto_Santos eu entendo que isso define quem é: Junior/Pleno/Sênior/Arquiteto. Após alguns anos surrando código vemos o que realmente importa. Não adianta fazer (go horse) o código funcionar e, algum tempo depois o cliente descobrir um bug por quebra de código legado.

L

@romero.dias, acho que isso define um desenvolvedor/analista preocupado com o que faz, não o seu nível de conhecimento. Alguns sabem todo o funcionamento das implementações e as regras das especificações java, mas fogem de testes unitários como o demo foge da crucis.

R

@Luis_Augusto_Santos conhecimento todos tem, porem existe a responsabilidade de aplicar esse conhecimento, isso é o que diferencia. Quem faz a diferença é quem coloca em prática, acompanha e monstra os resultados.

L

A responsabilidade é de todos. Cabe sim, a quem tem mais experiência, ensinar a quem não tem os meios mais adequados.
Aí voltamos ao que eu já disse: problemas na base, no ensino. Em qual faculdade/curso/etc você é ensinado a testar? Eu tive dois módulos de engenharia de software na faculdade. E lembro que tive três aulas de testes (caixa preta, caixa branca, cocoon e mais uns conceitos). De que isso adianta?
No meu TCC, isso nem foi cobrado (até por que, se fosse, eu teria reprovado).
Ou seja, professores não dão a mínima para os testes (amostragem em universo pessoal).

R

Isso ai! Eu costumo dizer: “Conhecimento bom é conhecimento disseminado”.
As faculdades não preparam o aluno para o mercado de trabalho, para as boa práticas de engenharia de software, etc…

A

Cara, que bons tempos o GUJ parece estar revivendo. Enfim, teve um tempo atrás onde uma conversa desse nipe já teria descambado para agressões pessoais e coisinhas sem sentido sem nenhum tipo de argumento. Coisas do tipo “bobo, feio e mal” mesmo… Kkkkkkkkkkkk

Já que assim não foi, todos mantiveram o nível e elevaram a discussão, vou me intrometer em algo que não entendo em sua plenitude, mas que acho que posso contribuir, devido aos meus 14 anos de mercado na fuça.

Já tentei estudar TDD por conta e em alguns cursos (que se mostraram bem fracos) durante os meus últimos 8 anos e sempre esbarro em algumas “coisitas” que espero assim colaborar com a discussão e quem sabe arranjar um “professor/tutor” no caminho.

Uma delas é código extremamente legado em produção com framework absurdamente antigo, classes com mais de 9.000 linhas de código (isso mesmo, 9K) e urgências pra ontem em produção. Tudo isso mais uma timeline agressiva em algumas outras situações sempre me fizeram deixar os testes automatizados pra “depois” no trampo atual.

Porém, uma coisa que levo comigo é sempre olhar para as referências em tudo o que pretendo fazer na minha vida e olhar o que eles estão fazendo. Cara, Martin Fowler com certeza é um desses caras e esse cara simplesmente me diz que testar é importante… Não, mais do que isso, é primordial, essencial, preciso, necessário… caras, eu com certeza levo isso muito a sério e por muito tempo o fato de não fazer os testes realmente me consumiu… Como eu me achava um bosta completo por meu código não ser coberto pelos testes… Aí todo um movimento do mercado e tals e eu sempre me sentindo pra trás.

Só que aí algumas coisas aconteceram e percebi que já tinha alguns softwares entrando em produção, gerando valor e até hoje estão lá com poucas manutenções traumáticas, uma qualidade que eu garanto (já que outra pessoa fez manutenções em tempo razoável) e eu meio que aceitei que eu tinha um buraco pra corrigir na minha carreira, afinal, ainda precisava aprender sobre testes automáticos, mas já não me achava tão bosta assim, afinal tinha sistema no ar gerando valor pra alguém e isso é minha métrica fundamental. Mas ok, minha busca continuou e eu precisava entender que se o sistema estava agregando valor sem testes, o que dizer se ele estivesse coberto por testes? Quão melhor não seria?

Foi quando achei alguém que veio trabalhar conosco que já tinha implementado testes em sua empresa anterior (sem nomes, pois não tenho autorização da pessoa pra isso)… Esse cara vendeu a ideia pra chefia, já que pra mim não era necessário, afinal eu já comprei a ideia e era/é uma meta na minha carreira profissional.

O fato é que eu enxerguei a luz no fim do túnel e o projeto de cobrir nossos Sistemas por testes automáticos estava saindo do papel.

Pra minha frustração 1 mês depois o cara simplesmente desistiu do processo, pois a nossa base de código tinha alguns empecilhos para que os testes fossem confiáveis (nunca entendi bem porque, exatamente por não conhecer a técnica) e ele desistiu saindo da empresa um tempo depois. Um outro colega que hoje trabalha com a gente e que também já trabalhou em uma empresa que cobria seus códigos com testes, simplesmente disse que precisaremos de um tempo para conseguir implantar a coisa da forma correta e que a quantidade de refactor que essa escrita de testes vai trazer, ele tem quase certeza que será impagável (ou seja, ninguém vai bancar $$ a alteração).

Enfim, no final das contas, ainda tenho alguns projetos pessoais que vou usando algumas coisas que acredito serem testes automatizados e me ajudam em alguns momentos, mas algumas coisas que faço (principalmente unitários) simplesmente não consigo enxergar valor nenhum na ponta final.

Como disse, meu problema hoje pode ser eu mesmo… Confesso que ainda não desenvolvo orientado por testes, pois ainda não sei COMO fazer isso, e as pessoas que já me disseram que o fazem no final das contas nunca conseguiram responder algumas de minhas perguntas práticas sobre nossos problemas da vida real. Como estamos com uma arquitetura nova na empresa, estamos nascendo ela já com os testes e em breve faremos uma rodada de validação com o time pra trocar ideia do valor que a prática nos trouxe e eu preciso me lembrar de voltar nesses posts e opinar.

É isso, se alguém quiser me ajudar no processo de aprendizagem, ficaria muito grato, mas ajuda seria pra me mostrar um problema real que foi resolvido com a cobertura de testes exemplificando situações que seriam desastrosas que os testes simplesmente evitaram essas situações em código real em produção. De preferência como o colega colocou aí cima, em uma base de código onde o prazo foi agressivo (não abusivo para a situação), o banco relacional não normalizado por um DBA que nem te conhece e as vezes te odeia e um código de 2005-2006 com um framework antigo e com features duvidosas.

O pedido é sério e garanto que o objetivo do post NÃO É (eu sei que pode parecer) desmerecer os testes automatizados. Eu realmente quero aprender na prática, não apenas na teoria, onde estou errando e o que não estou enxergando. Talvez meu bloqueio seja exatamente tentar resolver o passado com testes quando eu deveria estar me focando apenas em produtos que estão nascendo.

Talvez o bloqueio seja a desmotivação de não conseguir cobrir o passado com testes automáticos (pelo menos me parece bem mais difícil o fazer em código já antigo e mal escrito no legado) e me frustrar achando que não irei conseguir. Confesso, isso já me fez colocar a técnica em cheque, hoje não mais.

Por hora, apenas peço que realmente acabem esse discurso de que quem não pratica TDD é um BOSTA total e todos os que praticam são os profissionais que valem a pena… Acho que na prática, o que vale mesmo é software que na ponta o usuário esteja usando e tendo valor agregado, quer ele seja coberto por testes ou não…

REPITO, é importante, NÃO ESTOU desmerecendo testes automatizados, só quero mesmo entendê-los e, o quanto antes, usá-los em meus projetos.

Abraços a todos e obrigado pelo alto nível do papo :wink:

B

Sim.

L

@adriano_si realmente é muito mais difícil escrever testes em projeto legado que não tem testes, por isso que alguns na thread disseram sobre o benefício em escrever testes desde o princípio de um projeto (principalmente test-driven), pra evitar esse tipo de problema no futuro.

Mas em momento algum foi dito (ou até mesmo insinuado) que quem não escreve teste é um b*sta e só quem faz teste é valorizado. Na verdade foi apenas mostrado com experiências pessoais a importância de desenvolver test-driven, só isso. Nem todos pensam assim e isso é normal, afinal, de acordo com aquele velho clichê, muito comum por sinal aqui no GUJ: não existe bala de prata.

Num sistema legado, como não temos como voltar no passado :stuck_out_tongue:, o que resta é não escrever teste algum ou escrever testes quando for adicionar funcionalidade. Por exemplo, em projetos legados com muito código difícil de ser testado, eu costumo escrever testes apenas nos pontos onde vou adicionar feature ou corrigir um bug. Ao corrigir um bug, escrevo o teste que reproduz o bug (com os cenarios que forem necessarios no momento), e meu trabalho se resume em fazer o(s) teste(s) passar(em).

Obviamente podemos esquecer alguns cenários (nunca dá pra pensar em todos 100%), mas dessa forma já cobre bem alguns aspectos. Ao mexer numa classe que não tem teste, o que faço é escrever o teste para aquela classe apenas referente a feature/bug que estou trabalhando, não cobrindo as demais funcionalidades da classe, a não ser que outra funcionalidade externa seja dependência da que estou trabalhando. Posso não estar fazendo 100% correto, alguns TDDers que levam by the book podem não concordar comigo, mas é assim que tenho feito :smiley:

Eu citei apenas como eu faço com os unitários, sem contar que também nesse conta coloco teste de UI (ou aceitação, por exemplo Cucumber) e integração (chamada de controller, output across componentes, API’s).

EDIT:
Ah, sobre os testes de UI, nem sempre os faço, acabo analisando de acordo com a story que vou trabalhar. Se faz sentido, escrevo um cenario happy path no Cucumber e vou “descendo” no modo test-driven em integração e unitarios mas cobrindo mais cenarios de acordo com a especificação. Em alguns casos, eu me lembro de algum cenario que nao foi previsto e depois adiciono na especificação, mas é mais comum eu não lembrar. Quando estou fazendo pair-programming já é mais fácil pensar em mais cenarios, afinal são 2 pessoas pensando no problema.

Os testes de UI são muito caros, eu sempre tento discutir se são mesmo necessários pra determinada feature/bug a ser trabalhado. TDDers vão me matar kkkkk

K

Bom meu ponto anterior foi justamente por esse lado. Você talvez por ser o que tem costume de trabalhar, etc, acha que UI é secundário.

Mas isso no geral não é assim, é o que o usuário vê, pro usuário seu sistema pode ser perfeito por dentro mas se não atender o jeito que ele vai usar, ele simplesmente não vai comprar.

Interface com usuário ainda é mais critico quando o que você desenvolve é baseado em interação e a experiência do usuário como é o caso de desenvolver bibliotecas de UI (se não testar UI nesse caso, vai testar o que? hahah), coisas envolvendo gráficos, entrada do usuário, e que muitas vezes tem um aspecto subjetivo e estético.

Como falei há casos em que não só é caro, como é praticamente impossível testar de forma automatizada pelo fato de que bolar o teste em si não é trivial. Por isso minha conclusão é que TDD e BDD não atendem todos os casos, como querem prometer que fazem.

P

Escrever testes automatizados é muito caro pra quem trabalha com OO porque a maior parte do código é statefull, assim como a UI, e não pode ser testado de maneira confiável.

L

Não acho UI secundário de todo, mas existem features que podem ser testadas de forma mais barata utilizando outros métodos de teste. É questão de avaliar cada caso. Eu não disse em nenhum ponto desta thread que não testo UI at all.

Sim, isso é óbvio. Ninguém compra (ou ao menos não deveria comprar) algo que não atende as expectativas.

Com razão. Há casos que o custo de automatizar algo é muito alto, tem que avaliar alternativas mais baratas.

P

Pra testar um sistema stateful é fácil, vc o coloca num estado inicial e executa os testes desejados usando os parametros de entrada.

Depois é só rezar para os testes executem da mesma forma, toda vez para os mesmos parametros de entrada, e algum estado interno escondido não faz com que uma sequencia de eventos de entrada coloque o sistema num estado ruim, pq senão aí lascou :confused:

J

Isso é fato. Cada empresa tem seu cenário, geralmente em fábrica/consultoria, pela rotatividade de jovens, tem mais preocupação com teste do código que os próprios programadores usam, do que a funcionalidade para o usuário final. O ritmo desse ambiente exige diversas siglas para o desenvolvimento não virar o caos, além de se preocuparem mais em prever situações por não ter o feedback natural do cliente, diferente quando se está diretamente na atividade fim, onde você já faz parte do negócio. Claro que isso não é regra, tem empresas de TI que são pagas para se preocupar em manter a qualidade do que de fato vai ser usado.

A

Massa @leandrosp valeu por compartilhar a visão. Quando disse na verdade que as pessoas chamam de “bosta” quem não faz testes, estou falando de um senso comum que foi criado pela comunidade e que muitas vezes é propagado por grandes nomes da comunidade, não falei da Thread em si.

As vezes, perde-se mesmo apenas o senso de que nem todo código está nascendo hoje ou possui boas práticas, na verdade é exatamente o contrário.

Mas como disse, isso não me afeta mais… :stuck_out_tongue:

No mais, queria entender o que vocês chamam de testes de UI? Testar a interface de usuário com um Selenium da vida?

Continuem com a discussão, o nível está excelente e agregando bastante. Abraços a todos :wink:

E

I/O dificulta os testes porque você não tem controle sobre o que acontece durante I/O.

Chamadas ao relógio (new Date()), sistema de arquivos, gerador de números aleatórios, camada de rede, tudo isso, sim, pode dificultar os testes porque seu código deixa de ser determinístico, ou seja, retorna diferentes resultados para os mesmos parâmetros de entrada. Por isso, é importante você segregar código que faz i/o de código que não faz i/o. Encapsular essas chamadas atrás de classes que você pode substituir em tempo de execução é como geralmente resolvo o problema.

Armazenar estado em objetos não dificulta testes unitários, na minha experiência.

J

Exato, os analistas de qualidade usam Selenium webdriver por exemplo para automatizar testes de aceitação do que de fato será utilizado pelo usuário final. Não é um “conceito” novo, nada mais é do que automatizar o que era feito manualmente antes de forma repetitiva.

P

Sim, vc pode segregar o que conhece. Mas não o que está escondido.

Pra colocar um programa em determinado estado precisa criar objetos falsos, configura-los de uma maneira que é difícil de fazer de maneira automática pra cada teste case.

Pra mim parece bem complicado de se aplicar num projeto em larga escala. Não sei a opinião dos demais.

L

Se eu estou te entendendo, se o problema é devido ao fato de em OO não ser natural criar “funções puras” como nas linguagens funcionais, pelo menos até hoje consegui resolver isto aplicando single responsibility nas classes e componentes.

Ao invés de criar uma classe com método que faz um monte de coisa, criando mocks muito deep e tornando difícil fazer track do estado, eu tento ir dividindo em classes (que fazem sentido) até chegar num ponto onde uma classe XPTO é responsável por fazer algo mais complexo (ex. I/O) e, neste caso, o mock passa a ser mais simples pois toda a stack que ficou pra trás já está testada.

Até hoje consegui resolver assim nos modelos de domínio com os quais trabalhei. Talvez você esteja se referindo a um modelo complexo demais que torna mesmo impraticável testar. Vai saber…

L

O @leandronsp levantou uma questão bastante pertinente ao desenvolvimento e aos testes em si: a complexidade.
É sabido que uma das premissas menos faladas e utiizads da OO é a responsabilidade única das classes (lembro-me de um professor da faculdade que exigia, nos diagramas de classe, uma quarta divisão onde fosse definida a responsabildiade da classe).
Por que digo isso? Em sistemas legados (e muitos sistemas contemporâneos) é comum encontrarmos classe estilo canivete suíço: cobra escanteio, cabeceia e corre pro gol para defender. Fazem de tudo e, cada método, pode ter milhares de linhas, tornando-a um gigantesco monumento dos incorrigíveis comentários:

/* Não mexer que funciona assim */

É óbvio que coisas assim vão refletir em outros pontos, como a coesão e a acoplamento, em efeito cascata, vai atingir a criação de testes (e toda a funcionalidade do sistema).
Assim sendo, voltamos ao ponto de que temos erros mais profundos que afetam toda a cadeia: como alguém que mal sabe ou não quer saber como criar um código melhor vai ter visão para criar ou mesmo utilizar testes?

P

O problema é a falta de transparência referencial.

Meu ponto é que o sistema em produção ainda pode entrar num estado ruim devido algum estado interno escondido (e por ser escondido, não foi possível testar durante o desenvolvimento).

E outra, se vc precisa criar 50 classes pra modelar algo simples, estará aumentando as chances de introduzir bugs.

Enfim, tudo isso não é um problema se o objetivo é fazer um botão produzir um efeito glow quando clicado, mas para software crítico melhor evitar OO (pelo menos a versão mainstream que não possui transparência referencial, OO no estilo do Erlang/Akka ok).

E

E para raciocínio crítico, melhor evitar as generalizações. :slight_smile:

P

Mas não é uma generalização.

Software critico: classe especifica de software

Mainstream OO: versão especifica de OO, mais voltado para os interesses do mercado.

E

Bom, como a maior parte do software de missão crítica roda em linguagens imperativas, impuras e profanas e o mundo ainda não acabou, acho que você fez uma baita generalização.

P

Poderia citar um exemplo de software crítico feito em OO?

E

Defina software crítico.

P
L

Acho que já vi essa frase há muitos anos aqui no GUJ, não me lembro quando nem o autor. Parafraseando:

"é possível fazer qualquer coisa em qualquer linguagem que seja turing complete."

E isso vale para “software crítico”.
Encapsulando bem o comportamento dos objetos dá pra evitar o problema que você se refere à transparência referencial. Claro que, em certos tipos de linguagens funcionais é mais óbvio construir tais tipos de software devido à natureza funcional.

Por exemplo em Erlang/Elixir, você consegue ter tolerância a falhas e resiliência com o uso de supervisors e gen servers. Em Clojure, o Datomic com sua natureza imutável garante que dados não são perdidos.

Enfim, em certas linguagens ou tecnologias talvez seja menos custoso implementar tal tipo de software, mas não é impossível com OO se for bem feito, e claro, voltando à origem da thread, com testes escritos como devem ser.

Criado 19 de março de 2017
Ultima resposta 1 de abr. de 2017
Respostas 73
Participantes 14