httplacebo

The 'do nothing' HTTP client for Elixir.


License
MIT

Documentation

HTTPlacebo

HTTP client mocking tool for Elixir, based on HTTPotion, HTTPoison and inspired in HTTPretty.

Installation

  1. Add httplacebo to your list of dependencies in mix.exs:

    def deps do
      [{:httplacebo, "~> 0.1.0"}]
    end
    
  2. Ensure httplacebo is started before your application:

    def application do
      [applications: [:httplacebo]]
    end
    

Usage

iex> HTTPlacebo.start
iex> HTTPlacebo.register_uri(:get, "http://localhost:3000/posts/1", [body: ~s({"post": {"title": "First Post"}}), headers: [{"Content-Type", "application/json"}]])
iex> HTTPlacebo.get! "http://localhost:3000/posts/1"
%HTTPlacebo.Response{
  body: "{\"post\": {\"title\": \"First Post\"}}",
  headers: [{"Content-Type", "application/json"}],
  status_code: 200
}
iex> HTTPlacebo.get! "http://localhost:3000/users"
%HTTPlacebo.Response{body: "Not Found", status_code: 404}
iex> HTTPlacebo.get "http://localhost:3000/users"
{:ok, %HTTPlacebo.Response{body: "Not Found", status_code: 404}}

You can also easily pattern match on the HTTPlacebo.Response struct:

case HTTPlacebo.get(url) do
  {:ok, %HTTPlacebo.Response{status_code: 200, body: body}} ->
    IO.puts body
  {:ok, %HTTPlacebo.Response{status_code: 404}} ->
    IO.puts "Not found :("
end

Using with existing applications

You can use HTTPlacebo as replacement for HTTPoison instead of mocking as described in the Mocks and Explicit contracts post by Jose Valim.

defmodule MyBlog.Post do
  @http_mod Application.get_env(:my_app, :http_mod)

  def get(id) do
    # ...
    @http_mod.get("http://myblog.com/posts/" <> id)
    # ...
  end
end

And now we can configure it per environment as:

# In config/dev.exs
config :my_app, :http_mod, HTTPoison

# In config/test.exs
config :my_app, :http_mod, HTTPlacebo

Wrapping HTTPlacebo.Base

You can also use the HTTPlacebo.Base module in your test modules in order to make cool API clients or something.

The following example wraps HTTPoison.Base in order to build a client for the GitHub API (Poison is used for JSON decoding):

defmodule GitHub.Client do
  use HTTPoison.Base

  def process_url(url) do
    "https://api.github.com" <> url
  end

  def process_response_body(body) do
    body
    |> Poison.decode!
    |> Enum.map(fn({k, v}) -> {String.to_atom(k), v} end)
  end
end

For your tests you can create a test module similarly:

defmodule GitHub.InMemoryClient do
  use HTTPlacebo.Base

  def process_url(url) do
    "https://api.github.com" <> url
  end

  def process_response_body(body) do
    body
    |> Poison.decode!
    |> Enum.map(fn({k, v}) -> {String.to_atom(k), v} end)
  end
end

And now we can configure it per environment as:

# In config/dev.exs
config :my_app, :github_client, GitHub.Client

# In config/test.exs
config :my_app, :github_client, GitHub.InMemoryClient

It's possible to extend the functions listed below:

defp process_request_body(body), do: body

defp process_response_body(body), do: body

defp process_request_headers(headers) when is_map(headers) do
  Enum.into(headers, [])
end

defp process_request_headers(headers), do: headers

defp process_response_chunk(chunk), do: chunk

defp process_headers(headers), do: headers

defp process_status_code(status_code), do: status_code

License

Copyright © 2016 Guillermo Iguaran <guilleiguaran@gmail.com>

This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the LICENSE file for more details.