Bookish spork

Build StatusCoverage Statuscodecov

Hex.pmHex.pmHex.pmHex.pm

An erlang library to test http requests. Inspired by Ruby's WebMock.

Suitable for Elixir.

Rationale

There are several ways to test your http interaction

The last approach is the best IMHO. It is absolutely http-client agnostic. It doesn't require internet connection or any external utilities.

bookish_spork provides you facilities to test your requests with real http server.

Usage

Bookish spork supports Erlang/OTP 20.3 or later.

First step: add to your rebar config

{profiles, [
{test, [
{deps, [
{bookish_spork, "0.2.2"}
]}
]}
]}.

Second: start server in your tests.

bookish_spork:start_server().

It starts process without link. Thus you can use it in init_per_group and in init_per_suite callbacks. Default port is 32002 but you can specify any port you like with bookish_spork:start_server/1

Stub request

The simplest stub you can do is

bookish_spork:stub_request().

It will stub your requests with 204 No Content response with empty body.

If you need specify response you easily can do this:

bookish_spork:stub_request(Status, Headers, Content).

Capture request

As usual the main goal is to test that you send the correct request

{ok, Request} = bookish_spork:capture_request().

It returns you an opaque structure of the request. You can inspect it with

Bypass comparision

An elixir library bypass does pretty much the same. And illustrates the same approach. It starts a cowboy web-server to replace a real service for test

But bookish_spork has some advantages:

Examples

Setup and teardown

init_per_group(_GroupName, Config) ->
{ok, _} = bookish_spork:start_server(),
Config.
end_per_group(_GroupName, _Config) ->
ok = bookish_spork:stop_server().

Set expectation

init_per_testcase(random_test, Config) ->
bookish_spork:stub_request(200,
<<"{\"value\": \"Chuck Norris' favourite word: chunk.\"}">>),
Config.

Make assertions

random_test(_Config) ->
?assertEqual(<<"Chuck Norris' favourite word: chunk.">>, testee:make_request()),
{ok, Request} = bookish_spork:capture_request(),
?assertEqual("/jokes/random", bookish_spork_request:uri(Request)).

As you can see there are two types of assertions:

More complex expectations

There are cases when the testee function initiates more than one request. But if you know the order of your requests, you can set several expectations

bookish_spork:stub_request(200, <<"{\"value\": \"The first response\"}">>),
bookish_spork:stub_request(200, <<"{\"value\": \"The second response\"}">>).

The library will response in the order the stubs were defined.

Sometimes you can't guarantee the order of requests. Then you may stub request with the fun

bookish_spork:stub_request(fun(Request) ->
case bookish_spork_request:uri(Request) of
"/bookish/spork" ->
bookish_spork_response:new(200, <<"Hello">>);
"/admin/sporks" ->
bookish_spork_response:new(403, <<"It is not possible here">>)
end
end)

Elixir example

defmodule ChuckNorrisApiTest do
use ExUnit.Case
doctest ChuckNorrisApi
setup_all do
{:ok, _} = :bookish_spork.start_server
{:ok, %{}}
end
test "retrieves a random joke" do
:bookish_spork.stub_request(200, "{
\"value\": \"Chuck norris tried to crank that soulja boy but it wouldn't crank up\"
}")
assert ChuckNorrisApi.random == "Chuck norris tried to crank that soulja boy but it wouldn't crank up"
{:ok, request} = :bookish_spork.capture_request
assert :bookish_spork_request.uri(request) == '/jokes/random'
end
end

For more details see examples dir.