httpmock

HTTP mocking for Elixir


Keywords
elixir, httpoison, mock, phoenix
License
Apache-2.0

Documentation

HTTPMock

Installation

def deps do
  [
    {:httpmock, "~> 0.1.5"}
  ]
end

Using

  1. Create a router with mocked endpoint in test/support
defmodule Myapp.APIMockTest do
  @moduledoc """
    Mock Api
  """
  use HTTPMock, behaviour: :httpoison

  endpoint "https://jsonplaceholder.typicode.com" do
    get "/todos/:id", __MODULE__, :get_todo
    get "/users/:id", __MODULE__, :get_user
  end

  def get_todo(_conn, %{"id" => id}) do
    data = ~s({\n  \"userId\": #{id},\n  \"id\": #{id},\n  \"title\": \"delectus aut autem\",\n  \"completed\": false\n})
    {:ok, %{body: data, status_code: 200}}
  end
  def get_user(_conn, %{"id" => id}) do
    data = ~s({\n  "id": #{id},\n  "name": "Leanne Graham",\n  "username": "Bret",\n  "email": "Sincere@april.biz",\n  "address": {\n    "street": "Kulas Light",\n    "suite": "Apt. 556",\n    "city": "Gwenborough",\n    "zipcode": "92998-3874",\n    "geo": {\n      "lat": "-37.3159",\n      "lng": "81.1496"\n    }\n  },\n  "phone": "1-770-736-8031 x56442",\n  "website": "hildegard.org",\n  "company": {\n    "name": "Romaguera-Crona",\n    "catchPhrase": "Multi-layered client-server neural-net",\n    "bs": "harness real-time e-markets"\n  }\n})
    {:ok, %{body: data, status_code: 200}}
  end
end

With mimic

  1. Using Mimic to mimetize HTTPoison in test/test_helper.exs
  Mimic.copy(HTTPoison)
  ExUnit.start()
  1. use the mimic
  test "greets the world" do
    Mimic.stub_with(HTTPoison, Myapp.APIMockTest)
    assert {:ok, %{ status_code: 200}} = HTTPoison.get("https://jsonplaceholder.typicode.com/todos/1")
  end

Without mimic

  1. Set on top of you module @httpoison const to get env from test.exs

in your file that use httpoison

defmodule MyappWeb.Api.Client do
  @moduledoc """
    Myapp Web Api Client
  """
  @httpoison Application.compile_env(:myapp, :httpoison, HTTPoison)

  def get_todo(id) do
    @httpoison.get("https://jsonplaceholder.typicode.com/todos/#{id}")
    ## instead of
    # HTTPoison.get("https://jsonplaceholder.typicode.com/todos/#{id}")

  end
end
  1. set env in your config/test.exs
  .
  .
  .
  config :myapp, :httpoison, Myapp.APIMockTest
  1. Use on test
  test "greets the world" do
    assert {:ok, %{ status_code: 200}} = MyappWeb.Api.Client.get_todos(1)
  end

HTTPMock.State

The HTTPMock.State manage the state for your mocked api.

  1. Set the state in test/support/my_state.ex (my_state.ex is an example of namefile)
  defmodule Myapp.MyState do
    use HTTPMock.State
    entity(:todos, default: [], key: :id) ## the field :id is required by now
  end

The behaviour HTTPMock.state provide functions to manager state of 'table'

  • all()
  • all(entity)
  • one(entity, id)
  • create(entity, params)
  • delete(entity, id)
  • update(entity, id, params)
  • reset()

The reset() function should be called in setup, before tests, to cleaning state.

Create a new record

  :ok = Myapp.MyState.create(:todos, %{id: "uuid", title: "my_title"}) ## the only :id field is required. The other fields, follow your needs

I prefer create a new function on Myapp.MyState, because native create function return :ok, and then I want to return the last created record.

  defmodule Myapp.MyState do

   ...

   def new(params) do
     id = "uuid_generated"
     create(:todos, %{id: id, title: "my_title"})
     one(:todos, id)
   end

Get a previous record

with the Id of record

  returned_element= Myapp.MyState.one(:todos,id) ## or nil

Update a previous record

with the Id of record

  :ok = Myapp.MyState.update(:todos, id, %{title: "updated title"})

Delete a previous record

with the Id of record

  :ok = Myapp.MyState.delete(:todos, id)

Get all from table

with the Id of record

  :ok = Myapp.MyState.all(:todos)

Get all from all state

with the Id of record

  :ok = Myapp.MyState.all()
  1. Set the HTTPMock.State on Supervisor on test/test_helper.exs. We provide the HTTPMock.State.Supervisor

in test/test_helper.exs

  HTTPMock.State.Supervisor.start_link([Myapp.MyState])
  1. Using in your mocked router or in your tests, (rememeber that apply reset before tests) in any file, like test/myapp/some_test.exs
  defmodule Myapp.SomeTest do
  ...
    describe "some test" do
      setup do
        Myapp.MyState.reset()
      end
      test "testing something" do
        assert :ok = Myapp.MyState.create(:todos, %{id: "uuid", title: "my_title"})
        assert {:ok, %{body: json_encoded, status_code: 200 } } = MyappWeb.Api.Client.get_todos(1)
        assert %{id: "uuid", title: "my_title"} = Jason.decode!(json_encoded)}
      end
    end
  end

in mock route, update to use the state

defmodule Myapp.APIMockTest do
  @moduledoc """
    Mock Api
  """
  use HTTPMock, behaviour: :httpoison

  endpoint "https://jsonplaceholder.typicode.com" do
    get "/todos/:id", __MODULE__, :get_todo
  end

  def get_todo(_conn, %{"id" => id}) do
    returned_element= Myapp.MyState.one(:todos,id)
    data = returned_element |> Jason.encode!() ## Example using jason
    {:ok, %{body: data, status_code: 200} }
  end
  ...
end

That is it.