Desafios Comuns na Escrita de Testes Automatizados: Rumo à Clareza e Padronização - Parte 2
Tags: tdd, bdd, fullstack, programming
Published: 2024-02-23 01:17:48

Na [primeira parte](https://dev.to/ricardobsilva/desafios-comuns-na-escrita-de-testes-automatizados-rumo-a-clareza-e-padronizacao-parte-1-1f8d) nós vimos como testes são uma excelente forma de documentação, principalmente se os blocos *describe, context e it* estiverem organizados e com descrições claras, ajudando a dar clareza a respeito do objetivo daquela unidade de código em questão (e qual o papel dela em relação a outras unidades de código).

A ideia dessa segunda parte é continuar trabalhando padrões, agora focando nos seguintes pontos:

- Etapas de um teste automatizado
- Testes e implementação são interdependentes (e isso não deveria ser negociável)

**> Etapas de um teste automatizado**

Um teste automatizado é composto por três etapas: 

*Preparação(Setup)* - Aqui a gente carrega tudo aquilo que consideramos necessário para testar nossa unidade de código, seja a criação de um registro no banco, chamada para uma API externa, carregamento de arquivos e tudo mais que for imprescíndivel *antes* e para execução dos testes.

*Execução(Execution)* - Aqui é onde as coisas acontecem de fato. Nessa etapa o teste vai servir como gatilho para executar a unidade de código alvo, seja uma classe, um endpoint ou qualquer outro componente do sistema. Eu não vou dizer que absolutamente tudo pode ser testado, mas até hoje não vi algo que não possa. Porém, vale destacar que o verdadeiro objetivo é testar tudo aquilo que é referente a regras de negócio do sistema. Então não faz muito sentido testar métodos de uma biblioteca, por exemplo.

*Resultado(Assertion/Verification)* - Aqui é a etapa onde a gente verifica se o *comportamento* esperado da unidade de código aconteceu como esperado, de acordo com o cenário proposto, como por exemplo se algum registro foi criado/alterado no banco.

Exemplo:

```ruby
it 'must change the band name' do
  # Preparação: Cria uma banda com o nome 'The Rolling Stones'
  band = create(:band, name: 'The Rolling Stones')

  # Execução: Executa a unidade de código desejada
  updated_band = BandNameUpdater.new(band, 'Oasis').call

  # Resultado: Valida se o resultado saiu conforme esperado
  expect(updated_band.name).to eq('Oasis')
end
```

Independente do tamanho e complexidade do que está sendo testado, o objetivo é sempre se ater a essas três etapas na hora de construir ou modificar seus  testes, tentando sempre mantê-los **o mais simples possível**. E é aqui onde os testes começam a influênciar diretamente na qualidade do código do software, pois se estiver sendo difícil contemplar os possíveis comportamentos de uma unidade de código através dessas três etapas, isso pode ser o primeiro sinal de que essa unidade de código está assumindo multiplas responsabilidades e possivelmente uma quebra em unidades de códigos menores deverá ser  seu próximo passo, com o intuito de torna-la mais coesa.

**> Testes e implementação são interdependentes (e isso não deveria ser negociável)**

Essas três etapas também estão presentes em testes manuais. Se você usa ferramentas como o postman, por exemplo, você vai precisar criar objetos no banco, setar URL's, configurar headers (Preparação). Depois você vai clicar no botãozinho para fazer a requisição (Execução). E, por fim, você vai analisar tudo aquilo que veio na resposta (status 200? Json contendo determinado dado? Novo registro criado?).  

E dito isso, fica o questionamento:

Se você faz tudo isso manualmente para só depois escrever os testes automatizados, isso não seria um retrabalho?

Talvez tudo o que você precise é se forçar a colocar os testes automatizados desde o inicio do seu processo de desenvolvimento, por mais que sejamos constantemente tentados a abrir mão da qualidade (não só em testes) em detrimento de uma falsa sensação de velocidade de desenvolvimento, o que na prática não se concretiza, por conta do retrabalho que mais cedo ou mais tarde acontece.  Um exemplo muito comum? Lá vai:

1 - Dev estima tarefa com e sem testes

2 - Dev apresenta essas estimativas ao Product Owner (ou seja lá quem for a pessoa pra quem você estima tarefas)

3 -  Há um consenso de que escrever sem testes será mais rápido e que os testes serão escritos posteriormente

4 - A entrega é feita e lançada em produção com uma falsa sensação de rapidez

5 - A entrega resultou em um bug

6 - Para tudo para resolver o bug, dessa vez com muito mais pressão para entregar

7 - Surge o questionamento de quanto tempo levará para corrigir o bug

8 - De volta ao item 1 🤡
 

Ver os testes como separados da implementação é separar o código da qualidade do código, uma aposta difícil de ganhar, e eu achei importante trazer esse ponto antes de seguirmos com exemplos mais práticos pois, na minha percepção, fazer essa separação é o primeiro passo para ter testes frágeis, dificeis de manter e com pouca ou nenhuma padronização. 

Considere que testes e implementação **são** **a mesma coisa** e você estará colocando isso em prática mais cedo do que imagina.

Na parte três vamos ver na prática que tão importante quanto *saber* o que testar é saber o que *não* testar.

Até lá.