22 Feb 2025 - Raul Fernando
Mockando Dependências em Elixir com Mox
No desenvolvimento de aplicações Elixir, frequentemente nos deparamos com a necessidade de testar módulos que dependem de serviços externos, como APIs de terceiros ou bancos de dados. Para garantir que nossos testes sejam rápidos e confiáveis, podemos usar mocks. O Mox, uma biblioteca oficial da comunidade Elixir, nos permite criar mocks de maneira segura e eficaz.
Neste artigo, vamos aprender a configurar e utilizar o Mox para mockar dependências em testes.
Configurando o Mox
Para começar, adicione o Mox ao seu projeto adicionando a dependência no seu mix.exs:
# mix.exs
defp deps do
[
{:mox, "~> 1.0", only: :test}
]
end
Em seguida, rode mix deps.get para instalar a dependência.
Criando um Comportamento (Behaviour)
O Mox funciona com behaviours, que são contratos definidos para módulos em Elixir. Vamos criar um comportamento para um cliente HTTP fictício:
# lib/my_app/http_client.ex
defmodule MyApp.HttpClient do
@callback get(String.t()) :: {:ok, String.t()} | {:error, String.t()}
end
Esse comportamento define que qualquer módulo que o implemente deve ter uma função get/1 que retorna um {:ok, String.t()} em caso de sucesso ou {:error, String.t()} em caso de falha.
Criando o Mock
Agora, criamos um mock baseado no comportamento acima:
# test/support/mocks.exs
Mox.defmock(MyApp.HttpClientMock, for: MyApp.HttpClient)
Para garantir que o mock seja carregado corretamente em nossos testes, adicionamos essa linha ao test/test_helper.exs:
Code.require_file("support/mocks.exs", __DIR__)
Usando o Mock nos Testes
Agora podemos utilizar o mock nos testes. Vamos supor que temos um módulo que depende do HttpClient:
# lib/my_app/service.ex
defmodule MyApp.Service do
@http_client Application.compile_env(:my_app, :http_client, MyApp.HttpClient)
def fetch_data(url) do
case @http_client.get(url) do
{:ok, body} -> {:ok, "Processed: " <> body}
{:error, reason} -> {:error, reason}
end
end
end
Podemos então configurar o nosso teste para usar o mock:
# test/my_app/service_test.exs
defmodule MyApp.ServiceTest do
use ExUnit.Case, async: true
import Mox
setup :verify_on_exit!
test "fetch_data/1 returns processed data when HTTP request succeeds" do
MyApp.HttpClientMock
|> expect(:get, fn _url -> {:ok, "mock response"} end)
Application.put_env(:my_app, :http_client, MyApp.HttpClientMock)
assert MyApp.Service.fetch_data("http://example.com") == {:ok, "Processed: mock response"}
end
test "fetch_data/1 returns error when HTTP request fails" do
MyApp.HttpClientMock
|> expect(:get, fn _url -> {:error, "failed request"} end)
Application.put_env(:my_app, :http_client, MyApp.HttpClientMock)
assert MyApp.Service.fetch_data("http://example.com") == {:error, "failed request"}
end
end
Conclusão
O Mox é uma ferramenta poderosa para mockar dependências em testes Elixir, garantindo que possamos testar nossos módulos de forma isolada e previsível. Ele segue boas práticas, como garantir que todas as expectativas definidas sejam chamadas e que as implementações sejam validadas com behaviours.
Com essa abordagem, seus testes se tornam mais rápidos, confiáveis e fáceis de manter. 🚀