Patch - Ergonomic Mocking for Elixir
Patch makes it easy to replace functionality in tests with test specific functionality. Patch augments ExUnit with several utilities that make writing tests in Elixir fast and easy. Patch includes unique functionality that no other mocking library for Elixir provides, Patch's Super Powers.
Why use Patch instead of meck, Mock, Mockery, Mox, etc?
Patch starts with a very simple idea for how a patched function should work.
Patched functions should always return the mock value they are given.
Here are the key features of Patch.
- Easy-to-use and composable interface with sensible defaults.
- First class support for working with Processes.
- No testing code in non-test code.
In addition to these features which many libraries aspire to, Patch has 3 additional features that no other mocking library for Elixir / Erlang seem to have. These Super Powers are
- Patch mocks are effective for both local and remote calls. This means a patched function always resolves to the patch.
- Patch can patch private functions without changing their visibility.
- Patch makes it possible to test your private functions without changing their visibility via the
expose/2
functionality.
See the Mockompare companion project for a comparison of Elixir / Erlang mocking libraries. If there is a way to accomplish the following with another library, please open an issue so this section and the comparisons can be updated.
For more information about Patch's Super Powers see the Super Powers Documentation
Add patch to your mix.exs
def deps do
[
{:patch, "~> 0.6.0", only: [:test]}
]
end
After adding the dependency just add the following line to any test module after using your test case
use Patch
This library comes with a comprehensive suite of unit tests. These tests not only verify that the library is working correctly but are designed so that for every bit of functionality there is an easy to understand example for how to use that feature. Check out the User Tests for examples of how to use each feature.
Using Patch adds 10 core functions, 4 assertions, and 7 mock value builders to the test.
Core functions let us apply patches, patch processes, intercept messages, and query our patched modules.
Core Function | Description |
---|---|
expose/2 | Expose private functions as public for the purposes of testing |
fake/2 | Replaces a module with a fake module |
history/1,2 | Returns the call history for a mock |
inject/3 | Injects state into a GenServer |
listen/3 | Intercepts messages to a process and forwards them to the test process |
patch/3 | Patches a function so that it returns a mock value |
private/1 | Macro to call exposed private functions without raising a compiler warning |
real/1 | Resolves the real module for a patched module |
restore/1 | Restores a module to its pre-patched form |
spy/1 | Patches a module so calls can be asserted without changing behavior |
Assertions make it easy to assert that a patched module has or has not observed a call.
Assertion | Description |
---|---|
assert_called/1 | Asserts that a particular call has occurred on a mocked module |
assert_any_call/2 | Asserts that any call of any arity has occurred on the mocked module for a function name |
refute_called/1 | Refutes that a particular call has occurred on a mocked module |
refute_any_call/2 | Refutes that any call of any arity has occured on the mocked module for a function name |
Patched functions aren't limited to only returning simple scalar values, a host of Value Builders are provided for all kinds of testing scenarios. See the patch documentation for details.
Value Builder | Description |
---|---|
callable/1,2 | Callable that will be invoked on every patch invocation, dispatch mode can be customized |
cycle/1 | Cycles through the values provided on every invocation |
raises/1 | Raises a RuntimeException with the given message upon invocation |
raises/2 | Raises the specified Exception with the given attribtues upon invocation |
scalar/1 | Returns the argument as a literal, useful for returning functions |
sequence/1 | Returns the values in order, repeating the last value indefinitely |
throws/1 | Throws the given value upon invocation |
Patch comes with plenty of documentation and a Suite of User Tests that show how to use the library.
For a guided tour and deep dive of Patch, see the Guide Book
Tests automatically run against a matrix of OTP and Elixir Versions, see the ci.yml for details.
OTP \ Elixir | 1.7 | 1.8 | 1.9 | 1.10 | 1.11 | 1.12 |
---|---|---|---|---|---|---|
20 | ✅ | ✅ | ✅ | N/A | N/A | N/A |
21 | ✅ | ✅ | ✅ | ✅ | ✅ | N/A |
22 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
23 | N/A | N/A | N/A | ✅ | ✅ | ✅ |
24 | N/A | N/A | N/A | N/A | ✅ | ✅ |
Patch works by recompiling modules, this alters the global execution environment.
Since the global execution environment is altered by Patch, Patch is not compatible with async: true.
Up to version 0.5.0 Patch was based off the excellent meck library. Patch Super Powers required a custom replacement for meck, Patch.Mock
.
Patch also takes inspiration from python's unittest.mock.patch for API design.
See the Changelog