Testing API Web Calls in Elixir using meck

Elixir is a powerful functional programming language with an equally powerful test suite call ExUnit. it is possible to write unit and high level integration specs while developing.

As a Rubyist, one of the things that intrigued me was how do you test API calls as one could do in Ruby. I use WebMock a lot in Ruby tests - so is there something similar in Elixir / Erlang?

In a recent elixir project, I needed to do something similar and while reading through the test code for HTTPoison, I noticed a library called meck being used. Its a mocking library developed in Erlang. The following is a gist of an example of mocking api calls in Elixir:

# https://github.com/eproxus/meck
# add meck as a dependency in mix.exs

defmodule GithubIssuesTest do
  use ExUnit.Case
  import :meck

  setup_all do
    new(Issues.GithubIssues)
    on_exit fn -> unload end
    :ok
  end

  test "successful fetch with user and a project" do
    # already decoded from raw json using jsx in pipeline
    mock_response = {:ok, [
      [
        {"url", "https://api.github.com/repos/elixir-lang/elixir/issues/2956"},
        {"html_url", "https://github.com/elixir-lang/elixir/issues/2956"},
        {"id", 52467010},
        {"number", 2956}
        ]
      ]
    }

    # use meck to create a mock response
    expect(Issues.GithubIssues, :fetch, fn("elixir-lang", "elixir") -> mock_response end)

    # calls the actual function which makes the actual api call!
    # expect(Issues.GithubIssues, :fetch, fn("elixir-lang", "elixir") -> :meck.passthrough(["elixir-lang", "elixir"]) end)


    assert validate(Issues.GithubIssues)

    assert Issues.GithubIssues.fetch("elixir-lang", "elixir") == mock_response
  end
end

The module under test makes an API call to the Github api to fetch the latest issues for a given project.

After adding meck as a dependency, I import it into the test using import :meck.

In the setup block, I created a new mock object using

:meck.new(Issues.GithubIssues)

The on_exit call unloads :meck and restores the module its original functionality.

Within the test block, I stubbed out the actual method call using:

:meck.expect(Issues.GithubIssues, :fetch, fn("elixir-lang", "elixir") -> mock_response end)

Every call to Issues.Github.fetch(“elixir-lang”,”elixir”) will return the mock response and will not make the actual api call.

Like in WebMock, we can force the module to make the actual API call using:

:meck.passthrough(["elixir-lang", "elixir"])

Happy Hacking!