Desenvolvimento Dirigido por Testes (TDD) 🔁
Tags: tdd, javascript, testing, jest
Published: 2024-04-07 18:18:07

Desenvolvimento Dirigido por Testes, ou TDD, é como fazer um plano **antes** de começar uma viagem. Você **decide** para onde quer ir (o que quer que seu código faça), **verifica** se chegou lá (testa seu código), e se não, você **descobre** um caminho (escreve o código para fazer o teste passar). Depois, você olha ao redor para ver se pode **melhorar** algo na rota que escolheu (refatora o código).

Com TDD, você começa escrevendo um teste para a funcionalidade que quer adicionar ou o bug que precisa consertar. No início, esse teste **vai falhar** porque você ainda não escreveu o código que faz o que o teste espera, e isso é bom, por que significa que você tem uma meta clara.

A próxima etapa é escrever o código que satisfaz esse teste. Inicialmente, você pode até fazer algo bem simples, só para ver o teste passar. Isso pode parecer um pouco ao contrário, mas tem um bom motivo: ajuda a **garantir** que seu código só faz o que é realmente necessário e nada mais.

Depois que o teste passa, você tem a oportunidade de **melhorar** seu código, deixando-o mais limpo, organizado e eficiente. Isso é a **refatoração**. Como você tem os testes, pode refazer seu código com segurança, sabendo que se algo quebrar, os testes vão te avisar.

Por fim, TDD não é só sobre testar ou não esquecer de escrever testes. É uma forma de pensar sobre o design do seu software desde o início. Quando você escreve o **teste primeiro**, está pensando na interface e na usabilidade do seu código antes mesmo de implementá-lo. Isso pode levar a um design melhor e mais limpo.

---

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b71tevp9m9li33jjbbem.png)

## Testes Falhando 🔴

Você começa escrevendo um teste para a funcionalidade que quer criar, e esse teste vai falhar porque a funcionalidade ainda não existe. Isso é ótimo, porque agora você tem um claro ponto de partida e sabe exatamente o que seu código precisa alcançar. Aqui você também cria um esqueleto básico da sua classe ou função, só para o teste conseguir compilar.

---


## Testes Passando ✅ 
Agora é hora de fazer o teste passar, escrevendo código que atenda ao que o teste espera. No começo, o código pode ser relativamente **bem simples**. Não precisa ser perfeito, só precisa ver o teste ficar verde, mostrando que a funcionalidade básica está funcionando.

---

## Refatoração ♻️
Com os testes passando, você dá uma olhada crítica no seu código e vê como pode melhorá-lo sem mudar o que ele faz. Você **remove** redundâncias, limpa o código, e faz ajustes que deixam o código mais claro e fácil de manter.

---

## Exemplo: Carrinho de Compras 🛒
Imagine que você tem um objeto `Book` com propriedades como _título_, _ISBN_ e _preço_, e um objeto `ShoppingCart` onde você pode adicionar livros, calcular o preço total e remover livros.

**Estado Vermelho 🚩:** Primeiro, você escreve um teste para a nova funcionalidade do seu carrinho de compras. Inicialmente, esse teste **vai falhar** porque `ShoppingCart` ainda não tem o método `add` ou `getTotal`.

```
// ShoppingCart.test.js
const ShoppingCart = require('./ShoppingCart');
const Book = require('./Book');

describe('ShoppingCart', () => {
  it('calculates total price for books in the cart', () => {
    const book1 = new Book("book1", 10, "1");
    const book2 = new Book("book2", 20, "2");
    const cart = new ShoppingCart();
    cart.add(book1);
    cart.add(book2);
    expect(cart.getTotal()).toBe(30);
  });
});

```

---

**Estado Verde 🟢:** Depois, você implementa o mínimo necessário para fazer o teste passar. Aqui está uma implementação inicial para a classe `ShoppingCart` e a classe `Book`.
Com isso, quando você **rodar** o teste novamente, ele deve passar, porque `ShoppingCart` agora realiza a funcionalidade básica esperada.

```
// Book.js
class Book {
  constructor(title, price, isbn) {
    this.title = title;
    this.price = price;
    this.isbn = isbn;
  }
}

module.exports = Book;

```

```
// ShoppingCart.js
class ShoppingCart {
  constructor() {
    this.items = [];
    this.total = 0;
  }

  add(book) {
    this.items.push(book);
    this.total += book.price;
  }

  getTotal() {
    return this.total;
  }
}

module.exports = ShoppingCart;

```

---

**Estado Amarelo 🟡:** Pode ser interessante garantir que as propriedades do `Book` sejam privadas e acessíveis apenas através de métodos `getters` e `setters`.
Aqui está como poderia ser a **refatoração** da classe `Book`:

```
// Book.js (refatorado)
class Book {
  constructor(title, price, isbn) {
    this._title = title;
    this._price = price;
    this._isbn = isbn;
  }

  get title() {
    return this._title;
  }

  get price() {
    return this._price;
  }

  get isbn() {
    return this._isbn;
  }
}

module.exports = Book;

```

---


Após a refatoração, você deve rodar os testes novamente para garantir que tudo ainda está funcionando como deveria. Isso completa um **ciclo de TDD**. Agora, você pode adicionar mais funcionalidades ao carrinho, como remover itens, e para cada nova funcionalidade, você começaria um novo ciclo de TDD.

---

A maior parte desse conteúdo veio do livro "Engenharia de Software Moderna", escrito por Marco Tulio Valente. Esse livro é um tesouro cheio de dicas sobre como criar testes do jeito certo, usando o que tem de mais novo e eficiente no mundo da programação. Entretanto, a única diferença, é que o livro é em Java e aqui eu adaptei para utilizar Javascript.

---