double v0.1.1 Double

Double is a simple library to help build injectable dependencies for your tests. It does NOT override behavior of existing modules or functions.

Usage

The first step is to make sure the function you want to test will have it’s dependencies injected. This library requires usage of maps, but maybe in future versions we can do something different.

defmodule Example do
  @inject %{
    puts: &IO.puts/1,
    some_service: &SomeService.process/3,
  }

  def process(inject \ @inject)
    inject.puts.("It works without mocking libraries")
    inject.some_service.(1, 2, 3)
  end
end

Now for an example on how to test this interaction.

defmodule ExampleTest do
  use ExUnit.Case
  import Double

  test "example interacts with things" do
    inject = double
    |> allow(:puts, with: {:any, 1}, returns: :ok) # {:any, x} will accept any values of arity x
    |> allow(:some_service, with: [1, 2, 3], returns: :ok) # strictly accepts 3 arguments

    Example.process(inject)

    # now just use the built-in ExUnit methods assert_receive/refute_receive to verify things
    assert_receive({:puts, "It works without mocking librarires"})
    assert_receive({:some_service, 1, 2, 3})
  end
end

More Features

You can stub the same function with different args and return values.

double = double
|> allow(:example, with: [1], returns: [1])
|> allow(:example, with: [2], returns: [2])
|> allow(:example, with: [3], returns: [3])

double.example.(1) # 1
double.example.(2) # 2
double.example.(3) # 3

You can stub the same function with the same args and different return values on subsequent calls.

double = double
|> allow(:example, with: [1], returns: [1])
|> allow(:example, with: [1], returns: [2])

double.example.(1) # 2 the last setup is the first one to return
double.example.(1) # 1
double.example.(1) # 1 continues to return 1 until more return values are configured

Summary

Functions

allow(dbl, function_name, list)
double()