Essa é a quarta parte do projeto Rockelivery. Esse projeto faz parte do Bootcamp da Rocketseat, ministrado pelo professor Rafael Camarda.
Caso queira adquirir os cursos da Rocketseat com o meu cupom de desconto Acesse esse link
Conteúdo:
- 🙋♂️ Introdução
- 🕺 Testando o User Schema
- 🤖 Refatorando com ExMachina
- ➕ Testando o módulo Users.Create
- 🏭 Refatorando a Factory
- 🕹️ Testando o UsersController
- 😎 Testando a UsersView
- 👀 Verificando a Cobertura de Testes
🙋♂️ Introdução
Nessa Parte 4 do projeto vamos abordar o assunto de testes. Aconselho você a instalar uma biblioteca externa chamada ExCoveralls para verificar melhor a cobertura dos testes. Você também pode instalar a lib ExMachina para facilitar o setup dos testes. A instalação dessas bibliotecas você pode encontrar aqui: Instalação das ferramentas de uma pessoa desenvolvedora Elixir
Somente as funções públicas (def) serão testadas.
Quando começar a fazer um teste, deixe sempre falhar primeiro.
Usaremos o seguinte código nos arquivos de teste:
use Rockelivery.DataCase, async: true
Ele servirá para rodar os testes em paralelo. Isto é recomendado apenas para o banco de dados PostgreSQL. Se abrirmos o arquivo deste módulo Rockelivery.DataCase podemos ver essas informações:
 
 
Com o Rockelivery.DataCase também teremos a tradução dos erros do changeset:

🕺 Testando o User Schema
Vamos testar o arquivo: lib/rockelivery/user.ex.
Crie o arquivo de teste em test/rockelivery/user_test.exs.
Testando a função changeset/2
Neste teste teremos 3 cenários:
defmodule Rockelivery.UserTest do
  use Rockelivery.DataCase, async: true
  # Vamos testar aqui a função changeset com aridade 2
  describe "changeset/2" do
    # Primerio Cenário
    test "when all params are valid, returns a valid changeset" do
    end
    # Segundo Cenário
    test "when updating a changeset, returns a valid changeset with the given changes" do
    end
    # Terceiro Cenário
    test "when there are some error, returns an invalid changeset" do
    end
  end
end
Primeiro Cenário
Vamos para o primeiro cenário (quando todos os parâmetros são válidos retorna um changeset válido). Ao fazermos um teste, procuramos na maioria das vezes comparar dois lados. Se tentarmos comparar a resposta com a resposta esperada, ao rodarmos o comando mix test, podemos ver que o teste falha como queríamos, e que a resposta é mesmo um changeset:

 
 
Então, neste caso não faremos uma igualdade (==). Para este caso, a melhor forma de fazermos o teste é realizando Pattern Matching (=).
Agora, ao rodarmos o comando mix test, podemos ver que o teste falha novamente como esperado, mas que os dois lados agora são changesets, porém com dados diferentes em cada changeset:

Vamos agora fazer o teste passar, colocando os dados iguais nos dois lados:
Agora, ao rodarmos o comando mix test, podemos ver que o teste passa com sucesso!
Então, por enquanto o código do arquivo test/rockelivery/user_test.exs fica assim:
defmodule Rockelivery.UserTest do
  use Rockelivery.DataCase, async: true
  alias Ecto.Changeset
  alias Rockelivery.User
  describe "changeset/2" do
    # Primerio Cenário
    test "when all params are valid, returns a valid changeset" do
      params = %{
        age: 27,
        address: "Rua do Maiqui",
        cep: "12345678",
        cpf: "12345678901",
        email: "maiqui@tome.com.br",
        password: "123456",
        name: "Maiqui Tomé"
      }
      response = User.changeset(params)
      assert %Changeset{changes: %{name: "Maiqui Tomé"}, valid?: true} = response
    end
    # Segundo Cenário
    test "when updating a changeset, returns a valid changeset with the given changes" do
    end
    # Terceiro Cenário
    test "when there are some error, returns an invalid changeset" do
    end
  end
end
Segundo Cenário
Vamos agora para o segundo cenário (quando atualiza um changeset, retorna um changeset válido com os dados passados). Vamos começar deixando o teste falhar, colocando nomes diferentes:
 
 
Vamos colocar os nomes iguais pra ver o teste passar:

Agora o segundo cenário está finalizado:
 
  
Terceiro Cenário
Vamos agora para o terceiro cenário. Vamos testar agora as validações. Dessa vez vamos usar a variável expected_response pois usaremos a função Rockelivery.DataCase.errors_on(changeset) que comentamos na introdução.

Agora vamos ver o teste falhar:

Podemos perceber que não apareceu a mensagem dizendo que o password deve ter pelo menos 6 caracteres. O que aconteceu? vamos ver o schema do usuário:

Isso foi o nosso teste encontrando um bug que havíamos deixado :)
Após alterar o nome do campo de password_hash para password vamos executar novamente o comando mix test:

Vamos agora alterar o nosso teste para ele passar:

Terceiro teste finalizado com sucesso!
 
  
🤖 Refatorando com ExMachina
Crie o arquivo test/support/factory.ex:

O seu conteúdo deve ser semelhante:
defmodule Rockelivery.Factory do
  use ExMachina
  def user_params_factory do
    %{
      age: 27,
      address: "Rua do Maiqui",
      cep: "12345678",
      cpf: "12345678901",
      email: "maiqui@tome.com.br",
      password: "123456",
      name: "Maiqui Tomé"
    }
  end
end
Em test/rockelivery/user_test.exs:
Importe o módulo Rockelivery.Factory:
 
 
Executamos o comando mix test e tudo continua funcionando como antes:

➕ Testando o módulo Users.Create
Vamos testar o módulo lib/rockelivery/users/create.ex.
Crie o arquivo de teste em test/rockelivery/users/create_test.exs.
Nos testes desse módulo não vamos entrar em detalhes, pois os testes vão ser parecidos. Tente você fazer sozinho e depois olhe a resposta. Deixe sempre o teste falhar por primeiro.
O seu código deve ficar semelhante a esse:
defmodule Rockelivery.Users.CreateTest do
  use Rockelivery.DataCase, async: true
  import Rockelivery.Factory
  alias Rockelivery.{Error, User}
  alias Rockelivery.Users.Create
  describe "call/1" do
    test "when all params are valid, returns the user" do
      params = build(:user_params)
      response = Create.call(params)
      assert {:ok, %User{id: _id, age: 27, email: "maiqui@tome.com.br"}} = response
    end
    test "when there are invalid params, returns an error" do
      params = build(:user_params, %{age: 15, password: "123"})
      response = Create.call(params)
      expected_response = %{
        age: ["must be greater than or equal to 18"],
        password: ["should be at least 6 character(s)"]
      }
      assert {:error, %Error{result: changeset, status: :bad_request}} = response
      assert errors_on(changeset) == expected_response
    end
  end
end
🏭 Refatorando a Factory
Como as chaves dos parâmetros na nossa conexão vem em formato de string, vamos precisar refatorar a nossa factory para podermos usar ela nos testes do controller.
Em test/support/factory.ex:
Após essa alteração, ao executarmos o comando mix test percebermos que precisamos refatorar alguns testes:

Vamos refatorar em test/rockelivery/users/create_test.exs:

Vamos para o segundo teste que precisa ser refatorado:

Em test/rockelivery/user_test.exs:
 
 
Agora ao executarmos o comando mix test:

🕹️ Testando o UsersController
Vamos testar o módulo lib/rockelivery_web/controllers/users_controller.ex.
Crie o arquivo de teste em test/rockelivery_web/controllers/users_controller_test.exs.
No teste do controller usaremos RockeliveryWeb.ConnCase ao invés de Rockelivery.DataCase, que tem as mesmas funcionalidades mas com facilidades para testarmos um controller.

Testando a função create/2 - Primeiro Cenário
Vamos para o primeiro cenário: quando todos os parâmetros forem válidos, cria o usuário.
Vamos codar e deixar o teste falhar primeiro:
defmodule RockeliveryWeb.UsersControllerTest do
  use RockeliveryWeb.ConnCase, async: true
  import Rockelivery.Factory
  describe "create/2" do
    test "when all params are valid, creates the user", %{conn: conn} do
      params = build(:user_params)
      response =
        conn
        |> post("/api/users", params)
        |> json_response(:ok)
      assert "teste" = response
    end
  end
end
Vamos entender esse código:
- 
Como a rota é de criação então usamos a função Phoenix.ConnTest.post/3confira na documentação: https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#post/3- Existem também as funções get,delete,put...
 
- Existem também as funções 
- Phoenix.ConnTest.json_response/2afirma o código de status fornecido, que temos uma resposta json e retorna a resposta JSON decodificada se foi definida ou enviada. Veja mais na documentação: https://hexdocs.pm/phoenix/Phoenix.ConnTest.html#json_response/2
Agora vamos executar o comando mix test e ver o teste falhar:
Os nossos dados não foram decodificados para JSON pois houve um erro de status, ele esperava 201 (:created) e recebeu 200 (:ok). Então, vamos arrumar isso e vamos executar novamente mix test:
 
 
Copie o map no terminal e cole no lugar de "teste", ao salvar, a extensão do VSCODE ElixirLS irá formatar o código para nós. Como o id muda, então colocamos um _id ignorando o valor.

Ao executarmos mix test verificamos que os testes estão passando com sucesso:

Refatorando
Altere o seu código usando a função Routes.users_path().
 
 
Essa função já busca o path (caminho) da action :create. Então, se algum dia o path for alterado não preciramos alterar aqui.
Você pode estar se perguntando de onde está vindo essa função. Ela vem do ConnCase:
 
 
 
 
Mais detalhes você encontra na documentação: https://hexdocs.pm/phoenix/routing.html#path-helpers
Testando a função create/2 - Segundo Cenário
O segundo cenário é quando algum campo obrigatório não é preenchido.
Copie o map do terminal e cole no lugar de "teste":

Ou também você pode fazer usando igualdade:

Testando a função delete/2
Vamos fazer o teste desta função para conhecer uma nova funcionalidade. Para este teste vamos inserir um usuário no banco de dados.
Em test/support/factory.ex:
Adicione ao mesmo arquivo:
def user_factory do
    %User{
      age: 27,
      address: "Rua do Maiqui",
      cep: "12345678",
      cpf: "12345678901",
      email: "maiqui@tome.com.br",
      password: "123456",
      name: "Maiqui Tomé",
      id: "b721fcad-e6e8-4e8f-910b-6911f2158b4a"
    }
end
Em test/rockelivery_web/controllers/users_controller_test.exs:
describe "delete/2" do
    test "when there is an user with the given id, deletes the user", %{conn: conn} do
      id = "b721fcad-e6e8-4e8f-910b-6911f2158b4a"
      # inserindo a struct user no banco
      insert(:user)
      response =
        conn
        |> delete(Routes.users_path(conn, :delete, id))
        |> response(:no_content)
      assert response == ""
    end
end
Como o retorno é um :no_content (uma string vazia) estamos usando a função response ao invés do json_response.
😎 Testando a UsersView
Crie o arquivo de teste test/rockelivery_web/views/users_view_test.exs.
Dessa vez não faremos describe pois a view só tem a função render.
defmodule RockeliveryWeb.UsersViewTest do
  use RockeliveryWeb.ConnCase, async: true
  import Phoenix.View
  import Rockelivery.Factory
  alias RockeliveryWeb.UsersView
  test "renders create.json" do
    user = build(:user)
    response = render(UsersView, "create.json", user: user)
    assert %{
             message: "User created!",
             user: %Rockelivery.User{
               address: "Rua do Maiqui",
               age: 27,
               cep: "12345678",
               cpf: "12345678901",
               email: "maiqui@tome.com.br",
               id: "b721fcad-e6e8-4e8f-910b-6911f2158b4a",
               inserted_at: nil,
               name: "Maiqui Tomé",
               password: "123456",
               password_hash: nil,
               updated_at: nil
             }
           } = response
  end
end
👀 Verificando a Cobertura de Testes
Agora podemos verificar a nossa cobertura de testes com o comando
$ mix test --cover
Agora você já pode testar o restante da aplicação :)
 
 
              


















 
    
Top comments (0)