TDD com Elixir
Tags: elixir, tdd
Published: 2020-11-24 13:24:49

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fdwn6xyeh9sa92938ze2.jpeg)
Uma das coisas mais legais em Elixir é a facilidade para criar e executar testes automatizados (testes que são escritos como código).

Com isso, você pode fazer TDD (*Test-Driven Development* ou Desenvolmento Guiado por Testes) com facilidade. Não sabe o que é TDD? Leia o [livro do Maurício Aniche](http://bit.ly/2cN5FXl)!

O que vou mostrar aqui é o passo-a-passo para fazer TDD em Elixir.


# Passo 1: Criar o projeto

Uma vez tendo o Elixir instalado, você vai num terminal (no meu caso, um Ubuntu 20.04 dentro do WSL do Windows 10), e digita *mix new \<nome do projeto\>*. No caso abaixo, o nome do projeto é **calculadora**.


```bash
elixir@utfpr:~/DevTo$ mix new calculadora
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/calculadora.ex
* creating test
* creating test/test_helper.exs
* creating test/calculadora_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd calculadora
    mix test

Run "mix help" for more commands.
```

Pronto. O *mix* criou a pasta *calculadora* e dentro desta as pastas *lib* e *test*. Ou seja, o *mix* já separou para você o código que tem funcionalidades (pasta *lib*) do código de testes (pasta *test*). Além disso ele criou 5 arquivos. Os arquivos relevantes para nosso exemplo simples são apenas *lib/calculadora.ex* e *test/calculadora_test.exs*.

# Passo 2 - Rodar os Testes

Veja na imagem acima que o *mix* sugere que você, logo após a criação do projeto, vá para a pasta do novo projeto, *calculadora*, e execute os testes. Sim, o *mix* já cria testes para você. 

Vamos primeiro então rodar os testes que o *mix* criou:

```bash
elixir@utfpr:~/DevTo$ cd calculadora
elixir@utfpr:~/DevTo/calculadora$ mix test
Compiling 1 file (.ex)
Generated calculadora app
..

Finished in 0.08 seconds
1 doctest, 1 test, 0 failures

Randomized with seed 652059
elixir@utfpr:~/DevTo/calculadora$
```


Olha que legal! Passou em todos os testes! OK, só tem dois testes: um *doctest* e um teste *normal*.

# Passo 3: Remover o doctest

Vamos dar uma espiada nos arquivos gerados? Importante: estou usando o Elixir 1.11.2. Diferentes versões podem ter conteúdos diferentes nos arquivos gerados.

Arquivo *lib/calculadora.ex*:
```elixir
defmodule Calculadora do
  @moduledoc """
  Documentation for `Calculadora`.
  """

  @doc """
  Hello world.

  ## Examples

      iex> Calculadora.hello()
      :world

  """
  def hello do
    :world
  end
end

```

Como aqui estamos tratando do básico, eu vou apagar o *doctest*, ou seja, tudo que está entre  *@doc """* e *"""*. 

Arquivo *lib/calculadora.ex* passa a ser:
```elixir
defmodule Calculadora do
  @moduledoc """
  Documentation for `Calculadora`.
  """

  def hello do
    :world
  end
end

```

Rodemos os testes de novo:
```bash
elixir@utfpr:~/DevTo/calculadora$ mix test
Compiling 1 file (.ex)
.

Finished in 0.03 seconds
1 test, 0 failures

Randomized with seed 900374
elixir@utfpr:~/DevTo/calculadora$
```


Pronto. Agora só temos um teste. 

# Passo 4: Escrever o seu primeiro teste

Vamos dar uma olhada no teste? Está em *test/calculadora_test.exs*.

Isto é importante! O arquivo deve estar na pasta *test/* e seu nome deve terminar em *_test.exs*. Se não for assim, o arquivo contendo os testes não vai ser encontrado pela biblioteca que roda os testes.

Arquivo *test/calculadora_test.exs*:
```elixir
defmodule CalculadoraTest do
  use ExUnit.Case
  doctest Calculadora

  test "greets the world" do
    assert Calculadora.hello() == :world
  end
end
```

Como vemos acima, o teste gerado pelo *mix* tem descrição "greets the world" (tradução: "cumprimenta o mundo").

O único comando deste teste é um *assert*. Ele afirma (a tradução do Google Translate para *assert*) que se eu chamar a função *hello()* do módulo *Calculadora*, o resultado vai ser igual (==) ao átomo *:world*.

O teste que escreveremos simplesmente afirma que *2 + 2 = 4*:
```elixir
  test "2+2 é 4" do
    assert Calculadora.soma(2,2) == 4
  end
```
Com este teste, o arquivo *test/calculadora_test.exs* passa a ser:

```elixir
defmodule CalculadoraTest do
  use ExUnit.Case
  doctest Calculadora

  test "greets the world" do
    assert Calculadora.hello() == :world
  end

  test "2+2 é 4" do
    assert Calculadora.soma(2,2) == 4
  end
end
```

Uma das práticas de TDD é rodar os testes mesmo quendo você espera que eles falhem, como será o caso abaixo pois sequer criamos a função *Calculadora.soma/2*.

Rodemos os testes de novo:
```bash
elixir@utfpr:~/DevTo/calculadora$ mix test
warning: Calculadora.soma/2 is undefined or private
  test/calculadora_test.exs:10: CalculadoraTest."test 2+2 é 4"/1

.

  1) test 2+2 é 4 (CalculadoraTest)
     test/calculadora_test.exs:9
     ** (UndefinedFunctionError) function Calculadora.soma/2 is undefined or private
     code: assert Calculadora.soma(2,2) == 4
     stacktrace:
       (calculadora 0.1.0) Calculadora.soma(2, 2)
       test/calculadora_test.exs:10: (test)



Finished in 0.3 seconds
2 tests, 1 failure

elixir@utfpr:~/DevTo/calculadora$
```


Os testes falharam? Ótimo. Era o esperado.

# Passo 5: Fazer o teste passar usando Passos de Bebê.

A técnica Passos de Bebê (*Baby Steps*) sugere que você escreva o mínimo necessário para passar no teste. Neste caso, o mínimo necessário é escrever a função *Calculadora.soma/2* ignorando seus parâmetros e retornando 4:

```elixir
  def soma(_,_), do: 4
```
Arquivo *lib/calculadora.ex* passa a ser:
```elixir
defmodule Calculadora do
  @moduledoc """
  Documentation for `Calculadora`.
  """

  def hello do
    :world
  end

  def soma(_,_), do: 4
end
```


Rodemos os testes de novo:
```bash
elixir@utfpr:~/DevTo/calculadora$ mix test
Compiling 1 file (.ex)
..

Finished in 0.03 seconds
2 tests, 0 failures

Randomized with seed 371832
elixir@utfpr:~/DevTo/calculadora$ 
```

# Passo 6: Escrever o seu segundo teste 

O segundo teste que escreveremos afirma que *3 + 3 = 6*:
```elixir
  test "3+3 é 6" do
    assert Calculadora.soma(3,3) == 6
  end
```
Com este teste, o arquivo *test/calculadora_test.exs* passa a ser:

```elixir
defmodule CalculadoraTest do
  use ExUnit.Case
  doctest Calculadora

  test "greets the world" do
    assert Calculadora.hello() == :world
  end

  test "2+2 é 4" do
    assert Calculadora.soma(2,2) == 4
  end

  test "3+3 é 6" do
    assert Calculadora.soma(3,3) == 6
  end
end
```


Rodemos os testes de novo:
```bash
elixir@utfpr:~/DevTo/calculadora$ mix test
..

  1) test 3+3 é 6 (CalculadoraTest)
     test/calculadora_test.exs:13
     Assertion with == failed
     code:  assert Calculadora.soma(3, 3) == 6
     left:  4
     right: 6
     stacktrace:
       test/calculadora_test.exs:14: (test)



Finished in 0.06 seconds
3 tests, 1 failure

Randomized with seed 966924
elixir@utfpr:~/DevTo/calculadora$
```

Vejam que agora a mensagem de erro é diferente. O teste esperava *6* mas a função retornou *4*.

Como fazer o teste passar?
Agora basta implementar de fato a função *Calculadora.soma/2*:
```elixir
  def soma(num1, num2), do: num1 + num2
```
Arquivo *lib/calculadora.ex* passa a ser:
```elixir
defmodule Calculadora do
  @moduledoc """
  Documentation for `Calculadora`.
  """

  def hello do
    :world
  end

  def soma(num1, num2), do: num1 + num2
end
```

Rodemos os testes de novo:
```bash
elixir@utfpr:~/DevTo/calculadora$ mix test
Compiling 1 file (.ex)
...

Finished in 0.03 seconds
3 tests, 0 failures

Randomized with seed 498106
elixir@utfpr:~/DevTo/calculadora$
```

Passou em todos os testes!

OK, acho que por hoje é só. Daqui vocês já podem continuar.

Vejam que não deu tempo de falar de refatoração nem das diversas formas de afirmar/refutar usando [ExUnit](https://hexdocs.pm/ex_unit/ExUnit.html), o arcabouço (*framework*) de testes de unidade do Elixir.

E então, o que vocês querem saber mais sobre Elixir?


## PS: um vídeo relacionado

{% youtube Jk7uatUvg5c %}