View Source Patch

CI Hex.pm Version Hex.pm License HexDocs

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.

features

Features

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.

  1. Easy-to-use and composable interface with sensible defaults.
  2. First class support for working with Processes.
  3. 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

  1. Patch mocks are effective for both local and remote calls. This means a patched function always resolves to the patch.
  2. Patch can patch private functions without changing their visibility.
  3. 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

table-of-contents

Table of Contents

installation

Installation

Add patch to your mix.exs

def deps do
  [
    {:patch, "~> 0.13.1", only: [:test]}
  ]
end

quickstart

Quickstart

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 11 core functions, 10 assertions, 7 mock value builders, and 1 utility function to the test. These imports can be controlled, see the Customizing Imports for details.

See the Cheatsheet for an overview of how the library can be used and as a handy reference. Continue below for links to more in-depth documentation including the Guidebook.

core-functions

Core Functions

Core functions let us apply patches, patch processes, intercept messages, and query our patched modules.

Core FunctionDescription
expose/2Expose private functions as public for the purposes of testing
fake/2Replaces a module with a fake module
history/1,2Returns the call history for a mock
inject/3,4Injects a listener into a GenServer
listen/3Intercepts messages to a process and forwards them to the test process
patch/3Patches a function so that it returns a mock value
private/1Macro to call exposed private functions without raising a compiler warning
real/1Resolves the real module for a patched module
replace/3Replaces part of the state of a GenServer
restore/1,2Restores an entire module or just a function within a module to its pre-patched form
spy/1Patches a module so calls can be asserted without changing behavior

assertions

Assertions

Assertions make it easy to assert that a patched module has or has not observed a call.

AssertionDescription
assert_any_call/1Asserts that any call of any arity has occurred on the mocked module for a function name (preferred macro)
assert_any_call/2Asserts that any call of any arity has occurred on the mocked module for a function name (advanced use cases)
assert_called/1Asserts that a particular call has occurred on a mocked module
assert_called/2Asserts that a particular call has occurred a given number of times on a mocked module
assert_called_once/1Asserts that a particular call has occurred exactly once on a mocked module
refute_any_call/1Refutes that any call of any arity has occurred on the mocked module for a function name (preferred macro)
refute_any_call/2Refutes that any call of any arity has occurred on the mocked module for a function name (advanced use cases)
refute_called/1Refutes that a particular call has occurred on a mocked module
refute_called/2Refutes that a particular call has occurred a given number of time on a mocked module
refute_called_once/1Refutes that a particular call has occurred exactly once on a mocked module

value-builders

Value Builders

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 BuilderDescription
callable/1,2Callable that will be invoked on every patch invocation, dispatch and evaluation modes can be customized
cycle/1Cycles through the values provided on every invocation
raises/1Raises a RuntimeException with the given message upon invocation
raises/2Raises the specified Exception with the given attribtues upon invocation
scalar/1Returns the argument as a literal, useful for returning functions
sequence/1Returns the values in order, repeating the last value indefinitely
throws/1Throws the given value upon invocation

utility-functions

Utility Functions

Patch comes with some utilities that can assist when tests aren't behaving as expected.

Utility FunctionDescription
debug/0,1Enable or Disable debug mode for a given test

customizing-imports

Customizing Imports

By default, Patch will import the functions listed in the previous sections. Imports can be customized through the :only, :except and :alias options.

:only and :except work similiarly to how they work for the import except the values are either a list of symbol atoms or the special atom :all.

Here's how only the expose, patch, and private symbols can be imported.

use Patch, only: [:expose, :patch, :private]

Here's how every symbol except throws can be imported

use Patch, except: [:throws]

Patch also allows you to alias imported symbols, to import patch as mock the following would be used.

use Patch, alias: [patch: :mock]

guide-book

Guide Book

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

support-matrix

Support Matrix

Tests automatically run against a matrix of OTP and Elixir Versions, see the ci.yml for details.

OTP \ Elixir1.71.81.91.101.111.121.131.141.151.16
20N/AN/AN/AN/AN/AN/AN/A
21N/AN/AN/AN/AN/A
22N/AN/AN/A
23N/AN/AN/AN/AN/A
24N/AN/AN/AN/AN/A
25N/AN/AN/AN/AN/AN/A
26N/AN/AN/AN/AN/AN/AN/A

limitations

Limitations

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.

prior-art

Prior Art

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.

contributors

Contributors

Patch is made better everyday by developers requesting new features.

  • daisyzhou
    • Suggested the new function pass through behavior introduced in v0.9.0
  • likeanocean
    • Suggested assert_called/2, assert_called_once/1, refute_called/2, and refute_called_once/1 introduced in v0.7.0
  • birarda
    • Suggested assert_any_call/2, refute_any_call/2 introduced in v0.2.0
    • Suggested listen/1 introduced in v0.13.0 to listen without a target.
  • kianmeng
    • Corrected several typographical errata
    • Improved the ci.yml, brining it up to date with best practices.
  • Dorgan
    • Reported erratum in the Patch Cheatsheet
  • Luca Corti
    • Reported an issue with warning being emitted by the library on Elixir 1.16 which served as the basis for a bugfix in v0.13.1

If you have a suggestion for improvements to this library, please open an issue.

changelog

Changelog

See the Changelog