Os testes te guiam — Alguns motivos a mais para você usar TDD e BDD

O Desenvolvimento orientado a testes — Test Driven Development (TDD) — é um conceito do desenvolvimento de software, usado principalmente quando falamos em QA (Quality Assurance). Também anda em conjunto com o desenvolvimento ágil, pois ele, juntamente com a integração contínua formam a base de manutenção do código em ambientes distribuídos.

Em teoria, times trabalhando no desenvolvimento dos testes ligados a um ambiente de integração com outros desenvolvedores contribui para que nenhuma funcionalidade deixe de cumprir seu papel, pois já foi testada de forma automatizada anteriormente.

O TDD não serve apenas para cobrir tua aplicação com testes automatizados. Ele guia o seu design.

Este artigo tenta reforçar a importância do desenvolvimento orientado a testes em um momento em que existe bastante controvérsias do uso ou não dele no desenvolvimento, principalmente quando não queremos “perder tempo”. Há quem diga que o TDD já morreu :/

Veja algumas opiniões:

A questão é que ele muda a forma de desenvolver e você precisa escolher a maneira que ele melhor se adapte para se tornar seu aliado, e não um inimigo.

Já vi diversas opiniões bastante controversas, dizendo que o TDD fez com que tivéssemos que produzir muito mais código de teste do que da própria aplicação. Muitas vezes sim, isto é verdade, mas não podemos ignorar que, quando aplicado de forma inteligente, ele pode fazer toda diferença na escalabilidade, mesmo sendo mais código, são menos Bugs, e código de teste não é código de produção, por isto muitas vezes as exigências com ele devem ser menores para não se tornar algo mais complicado que o código que ele testa em si.

E digo mais! Ele pode ser mais importante para o crescimento da sua aplicação do que próprias questões de infraestrutura, sobre qual linguagem escala mais etc. Vou falar posteriormente por que o Ruby é ainda a melhor linguagem para testes.

Uma das vantagens do TDD é testar funcionalidades e situações que você não tem uma forma fácil de testar de outra forma, como retornos de API’s complexas e situações de borda da sua aplicação. Sem contar ainda quando se fala de aplicações financeiras, o TDD para mim passa a ser essencial, pois testar pagamentos de forma manual é uma das tarefas mais custosas de manutenção e muitas vezes custa mais dinheiro do que evitar o TDD por “levar mais tempo”.

Quando a complexidade da aplicação aumenta, alguns testes e comportamentos que são difíceis de reproduzir se tornam códigos e dependências que também passam a ser difíceis de manter e acompanhar. Muitas vezes o problema dos testes está como eles evidenciam uma aplicação complicada demais e não flexível o suficiente para ser testada.

Os testes são ferramentas para contribuir e não para escravizar e impedir que mudanças rápidas sejam feitas. Aliás, ele nos dá mais segurança (quando não geramos falsos positivos) para sempre melhorar o código de forma contínua. O Refactoring é uma das etapas mais importantes do TDD.

  1. Ruby
    O Ruby, principalmente no Rails ganha aqui não pela linguagem, mas pela sua comunidade que criaram ferramentas diversas para se testar e popularizou o BDD.
    O BDD faz com seu código seja bastante claro em relação ao teste que você está fazendo (assertivo) e a facilidade de se interagir com o comportamento real do usuário.
  2. Python
    Para mim é a melhor para testes juntamente com o Rails, ainda mais pela experiência que tive na Globo.com com a criação de Frameworks em Python em que contribuimos com a comunidade criando ferramentas de testes poderosas para realizar testes de interface utilizando linguagem natural.
  3. React
    O React, com o Jest e as bibliotecas que simulam o DOM fizeram com que ficasse muito fácil fazer asserções no conteúdo do DOM gerado pelo React. Também é bem prático testar componentes isolados e diferentes estados. Outro fator que surpreendi muito foi teste com react para aplicações nativas. O Jest tem também a ferramenta incrível de snapshot, que valida estados definidos em um determinado momento.

O TDD para funcionar de forma eficiente precisa de muito pragmatismo pra se beneficiar de todo seu potencial. Precisamos definir também um escopo para ele. No fim, para mim não importa a quantidade de testes e cobertura e sim a resposta que você precisa para dar um passo a frente, e não se o teste é unitário, funcional ou de interface.

Então temos alguns motivos e práticas importantes quando utilizamos o TDD:

  1. O motivo de fazer o teste primeiro é perguntar para a máquina o retorno de algo que você precisa para seguir adiante com seu código.
  2. Não é burrice você fazer um teste sabendo que ele vai quebrar, mas esperar seu resultado antes de dar o próximo passo mas resolver um problema de cada vez.
  3. O TDD faz você entender melhor o código dos outros
  4. Conseguimos também relembrar melhor nosso código quando usamos TDD. Eu tento muitas vezes deixar um teste falhando para corrigir no outro dia como sendo o próximo passo que preciso seguir para completar um código.
  5. Com o TDD nós podemos testar situações que não precisamos reproduzir. Se o retorno de um request, por exemplo, for x, eu posso testar diferentes retornos sem necessariamente passar todos os estados
  6. Uma das questões mais difíceis de praticar o TDD são aplicações com comportamentos randômicos, ou situações que não conseguimos reproduzir no fluxo direto

O TDD, como a própria definição sugere (driven), é desenvolvimento orientado a testes. Sendo assim, o teste passa a ser a primeira parte do código. O processo é basicamente:

  1. Crie o teste com a situação desejada
  2. Execute o código e veja que ele falha pelo motivo esperado
  3. Faça uma solução simples que garante que ele tenha o retorno desejado
  4. Execute o código e veja que o teste passa
  5. Mude a parte testada para que ele seja uma solução mais próxima da real
  6. Execute o código e veja que o teste passa

São 6 passos do fluxo do TDD para seguir adiante para cada funcionalidade que você precisa criar ou modificar. Estes testes correspondem a diferentes níveis da aplicação que reflete diferentes níveis, como funcional, integração e unitários. O nome e tipo de teste pode variar de empresa a empresa.

Você pode tirar todo o proveito destes passos, mas não se apegue a eles se alguma situação você precisar seguir de uma forma diferente e se algum passo se tornou muito difícil. Chega uma hora que você vai ver que o processo não tem como ser perfeito, mas ajudou e foi útil enquanto durou e você pode retomá-lo novamente. O TDD é sempre desafiante e questionado no desenvolvimento. Muitas vezes eu quero fazer prototipagem rápida e não vou comprometer a qualidade se não fiz os testes seguindo a risca o processo. Dizer que por isto o TDD tem que morrer e parar de ser usado já é ir para o outro extremo. Ele amadurece com a experiência.

O fato é que sempre teremos uma resistência natural a uma nova abordagem de desenvolvimento e o TDD pode parecer um pouco “radical”, mas depois que você se acostuma com o processo, vê que ele passa a fazer parte do dia a dia e gera uma certa “desconfiança” natural ao seu código. Isto por que naturalmente com o TDD você começa a ver que para fazer grandes mudanças e refazer certas partes do sistema, pensar em não ter testes se torna algo abominável e assustador.

Apesar de ver como algo essencial, não usar TDD não faz com que o código e a qualidade seja melhor, mas usá-lo de forma eficiente é uma garantia mínima e eficaz de automatizar os processos de teste. Sem o TDD, aplicações complexas são bastante questionáveis.

Citizen Scientist and Software Engineer, looking for the limits beyond mind and machine exploring the world

Citizen Scientist and Software Engineer, looking for the limits beyond mind and machine exploring the world