Simple mocking library for asynchronous testing in Elixir.
Readme and documentation for last stable version are available on hex
defmodule/2
nor Module.create/3
)MIX_ENV=test
Erlang/OTP 21 contains some changes that prevent the package from functioning as before.
Please check migration guide.
def deps do
[
{:mockery, "~> 2.3.0", runtime: false}
]
end
# lib/my_app/foo.ex
defmodule MyApp.Foo do
use Mockery.Macro
alias MyApp.Bar
def baz, do: mockable(Bar).function()
end
defmodule MyApp.Controller do
# ...
use Mockery.Macro
def all do
mockable(MyApp.UserService).users()
end
def filtered do
mockable(MyApp.UserService).users("filter")
end
end
# tests
defmodule MyApp.ControllerTest do
# ...
import Mockery
test "mock any function :users from MyApp.UserService" do
mock MyApp.UserService, :users, "mock"
assert all() == "mock"
assert filtered() == "mock"
end
test "mock MyApp.UserService.users/0" do
mock MyApp.UserService, [users: 0], "mock"
assert all() == "mock"
refute filtered() == "mock"
end
test "mock MyApp.UserService.users/0 with default value" do
mock MyApp.UserService, users: 0
assert all() == :mocked
refute filtered() == :mocked
end
test "chaining multiple mocks for same module" do
UserService
|> mock([users: 0], "mock value")
|> mock([users: 1], "mock value")
# ...
end
end
Instead of using a static value, you can use a function with the same arity as original one.
defmodule Foo do
def bar(value), do: value
end
# prepare tested module
defmodule Other do
use Mockery.Macro
def parse(value) do
mockable(Foo).bar(value)
end
end
# tests
defmodule OtherTest do
# ...
import Mockery
test "with dynamic mock" do
mock Foo, [bar: 1], fn(value)-> String.upcase(value) end
assert parse("test") == "TEST"
end
end
# prepare tested module
defmodule Tested do
use Mockery.Macro
def call(value, opts) do
mockable(Foo).bar(value)
end
end
# tests
defmodule TestedTest do
# ...
import Mockery.Assertions
# use Mockery # when you need to import both Mockery and Mockery.Assertions
test "assert any function bar from module Foo was called" do
Tested.call(1, %{})
assert_called Foo, :bar
end
test "assert Foo.bar/2 was called" do
Tested.call(1, %{})
assert_called Foo, bar: 2
end
test "assert Foo.bar/2 was called with given args" do
Tested.call(1, %{})
assert_called Foo, :bar, [1, %{}]
end
test "assert Foo.bar/1 was called with given arg (using variable)" do
params = %{a: 1, b: 2}
Tested.call(params)
assert_called Foo, :bar, [^params]
# we need to use pinning here since assert_called/3 is a macro
# and not a regular function call and it gets expanded accordingly
end
test "assert Foo.bar/2 was called with 1 as first arg" do
Tested.call(1, %{})
assert_called Foo, :bar, [1, _]
end
test "assert Foo.bar/2 was called with 1 as first arg 5 times" do
# ...
assert_called Foo, :bar, [1, _], 5
end
test "assert Foo.bar/2 was called with 1 as first arg from 3 to 5 times" do
# ...
assert_called Foo, :bar, [1, _], 3..5
end
test "assert Foo.bar/2 was called with 1 as first arg 3 or 5 times" do
# ...
assert_called Foo, :bar, [1, _], [3, 5]
end
end
Every assert_called/x function/macro has its refute_called/x counterpart.
For more information see docs
Mockery.History module provides more descriptive failure messages for assert_called/{3,4} and refute_called/{3,4} that includes a colorized list of arguments passed to a given function in the scope of a single test process.
Disabled by default. For more information see docs
Useful when you need to use the same mock many times across different tests
defmodule Foo do
def bar, do: 1
def baz, do: 2
end
defmodule FooGlobalMock do
def bar, do: :mocked
end
# prepare tested module
defmodule Other do
use Mockery.Macro
def bar, do: mockable(Foo, by: FooGlobalMock).bar()
def baz, do: mockable(Foo, by: FooGlobalMock).baz()
end
# tests
defmodule OtherTest do
# ...
test "with global mock" do
assert Other.bar == :mocked
assert Other.baz == 2
end
end
Global mock module doesn't have to contain every function exported by the original module, but it cannot contain a function which is not exported by the original module.
It means that:
For advanced usage examples see