Extendable testing utilities for Plug
Fugue
is available in Hex and can be installed as:
- Add fugue your list of dependencies in
mix.exs
:
def deps do
[{:fugue, "~> 0.1.0"}]
end
Fugue
extends ExUnit.Case
by adding macros for calling Plug
applications in an extendable way.
defmodule Test.MyApp do
use Fugue, plug: MyApp
test "root response" do
request()
after conn ->
conn
|> assert_status(200)
end
test "users read response" do
user_id = "123" # this could come from a seed function
request do
path "/users/#{user_id}"
end
after conn ->
conn
|> assert_body_contains(user_id)
end
end
Notice the setup and assertions are separated by an after
keyword. The idea is to decouple the test case setup/generation from the actual test case execution. This ends up being useful in many ways. We could:
- generate cases on a single machine and distribute the requests around a cluster
- create benchmarks that exclude the setup time and just test the request rate (maybe even skip assertions entirely)
- serialize the requests into a file and run at a later time, or in a different language or service
- chain multiple tests together to create flow tests
- insert your crazy idea here
Fugue
exposes several overridable functions to support this behavior:
execute
is the lowest level hook. It receives the request struct and a function handle for assertions. The default behavior is to call the call/2
function followed by the assertions:
defmodule Test.MyApp do
use Fugue
defp execute(request, assertions, context) do
request
|> call(context)
|> assertions.()
end
end
call
receives the request struct and executes the request. In the case standard Plug
apps this would look something like:
defmodule Test.MyApp do
use Fugue
def call(request, _context) do
MyApp.call(request, [])
end
end
When using Fugue
, you may pass :plug
and :plug_opts
to the default call
implementation:
defmodule Test.MyApp do
use Fugue, plug: MyApp,
plug_opts: []
end
init_request
is passed the test context and can create the request struct. This is helpful for when something other than Plug.Conn
is used or the default values for Plug.Conn
need to be changed.
prepare_request
is called just before calling execute/3
which allows for any final modifications to the request to be made.