Coder Social home page Coder Social logo

web_spell's Introduction

WebSpell

Build Status

Ebert

WebSpell is an HTTP mocking library for Elixir. It is somewhat inspired by WebMock for Ruby, but adapted to the different programming environment of Elixir.

Motivation

When I started my first Elixir/Phoenix web app and started looking for ways to stub calls to external services, all I found was a few blog posts that told me to replace my HTTP client with a (static) module for testing like this:

defmodule TestStub do
  def get("http://example.com/1") do
    {:ok, "resource 1"}
  end

  def get("http://example.com/2") do
    {:ok, "resource 2"}
  end
  # etc.
end

This way my tests wouldn't hit any real endpoints and stay fast. This approach may work for projects communicating very little with the outside world, but not for what I'm doing. What you are doing essentially is creating fixtures, i.e. some static data that all your tests depend on. As your test suite grows, you need more of them, and whenever you change one, something breaks somewhere else.

So what I needed was a way to set up stubbed web requests that were different for every test case. Say hello to WebSpell.

Installation

The package can be installed by adding web_spell to your list of dependencies in mix.exs:

def deps do
  [{:web_spell, "~> 0.1.0"}]
end

Usage

Please note: the examples below use the Poison library for JSON parsing/encoding.

You start with a module that calls your HTTP client of choice:

defmodule MyWebClient do
  def get_user
    HTTPoison.get("http://example.com/user", {access_token: "123"}, {Accept: "application/json"})
  end
end

To start using WebSpell, make the HTTP client configurable by using a module attribute.

# a module that uses any HTTP client to make request
defmodule MyWebClient do
  @http_client Application.get_env(:my_app, :http_client) # either an actual HTTP client module or your mock module

  def get_user
    {:ok, response} = @http_client.get("http://example.com/user", {access_token: "123"}, {Accept: "application/json"})

    Poison.Parser.parse!(response.body)
  end
end

For development/production environments, configure your normal client:

# config/config.exs
use Mix.Config
import_config "#{Mix.env}.exs"

# config/prod.exs / config/dev.exs
use Mix.Config
config :my_app, http_client: HTTPoison

# config/test.exs
use Mix.Config
config :my_app, http_client: MockHTTPClient

And implement a mock HTTP client using WebSpell:

defmodule MockHTTPClient do
  use WebSpell

  def get(url, query, headers) do
    response = call_stubbed_request! %WebSpell.Request{method: :get, url: url, query: query, headers: headers}
    {:ok, response.body} # emulate the behavior of your production HTTP client
  end
end

Finally you can start writing tests using WebSpell:

defmodule MyWebClientTest do
  use ExUnit.Case, async: false # sorry, WebSpell can only handle one test at a time

  setup do
    MockHTTPClient.start_link()
    :ok
  end

  test "get_user returns user data" do
    MockHTTPClient.stub_request(
      %WebSpell.Request{
        method: :get,
        url: "http://example.com/user",
        query: {access_token: "123"}, # optional
        headers: {Accept: "application/json"} # optional
      },
      %WebSpell.Response{
        status_code: 200,
        body: Poison.encode!(%{"email" => "[email protected]"}) # convert to JSON
      }
    )

    user = MyWebClient.get_user

    assert user == {"email" => "[email protected]"}
    assert MockHTTPClient.received_request(
      %WebSpell.Request{method: :get,
                        url: "http://example.com/user",
                        query: {access_token: "123"},
                        headers: {Accept: "application/json"}})
    assert MockHTTPClient.received_no_request
      (%WebSpell.Requestr{method: :get, url: "http://example.com/account"})
  end
end

You can also specify a a timeout to wait for a request to be made asynchronously:

assert MockHTTPClient.received_request(
  %WebSpell.Request{method: :get,
                    url: "http://example.com/user",
                    },
                    200) # timeout in ms

For POST/PUT etc. you can pass in a body:

assert MockHTTPClient.received_request(
  %WebSpell.Request{method: :post,
                    url: "http://example.com/user",
                    body: Poison.encode!(%{email: "[email protected]"}),
                    headers: {Accept: "application/json"}})

Ideas

Add support for a few popular HTTP clients and return responses matching them instead of just WebSpell.Response.

web_spell's People

Contributors

langalex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

web_spell's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.