Mock is a simple library that allows you to test different methods responses by mocking the module. Here what it could look like to your example:
defmodule ShowMockLibraryTest do
use ExUnit.Case
alias HTTPoison
import Mock
defmodule Client do
def perform_request(query) do
url = "http://www.some-service?q=#{query}"
case HTTPoison.get(url) do
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
{:ok, body}
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, reason}
end
end
end
@ok_response %HTTPoison.Response{
status_code: 200,
body: "Some content"
}
@error_response %HTTPoison.Error{reason: "Some reason"}
test "should return the body if request returns 200" do
with_mock HTTPoison, get: fn _ -> {:ok, @ok_response} end do
assert {:ok, "Some content"} = Client.perform_request("some-query")
end
end
test "should return the reason if request returns an error" do
with_mock HTTPoison, get: fn _ -> {:error, @error_response} end do
assert {:error, "Some reason"} = Client.perform_request("some-query")
end
end
test "should add the query to the url before calling it" do
with_mock HTTPoison, get: fn _ -> {:ok, @ok_response} end do
assert {:ok, _body} = Client.perform_request("some-query")
assert_called(HTTPoison.get("http://www.some-service?q=some-query"))
end
end
test "should raise if any other response is received" do
with_mock HTTPoison, get: fn _ -> {:error, "Unknown Error"} end do
assert_raise CaseClauseError, fn ->
Client.perform_request("some-query")
end
end
end
end
If you use Tesla as your client, you can use their built-in mocks. See Tesla.Mock.
Another possible approach is to separate concerns in differents methods. You basically isolate code you can’t control from code that you can. This is what I mean:
defmodule SeparateConcernsTest do
use ExUnit.Case
alias HTTPoison
defmodule Client do
def perform_request(query) do
query
|> url()
|> HTTPoison.get()
|> handle()
end
def url(query), do: "http://www.some-service?q=#{query}"
def handle({:ok, %HTTPoison.Response{status_code: 200, body: body}}) do
{:ok, body}
end
def handle({:ok, %HTTPoison.Error{reason: reason}}), do: {:error, reason}
end
@ok_response %HTTPoison.Response{
status_code: 200,
body: "Some content"
}
@error_response %HTTPoison.Error{reason: "Some reason"}
test "should return the body if request returns 200" do
assert {:ok, "Some content"} = Client.handle({:ok, @ok_response})
end
test "should return the reason if request returns an error" do
assert {:error, "Some reason"} = Client.handle({:error, @error_response})
end
test "should add the query to the url" do
assert Client.url("some-query") == "http://www.some-service?q=some-query"
end
test "should raise if any other response is received" do
assert_raise CaseClauseError, fn ->
Client.handle({:error, "Unknown Error"})
end
end
end