All Projects → edgurgel → Mimic

edgurgel / Mimic

Licence: apache-2.0
A mocking library for Elixir

Programming Languages

elixir
2628 projects

Projects that are alternatives of or similar to Mimic

ts-mock-imports
Intuitive mocking library for Typescript class imports
Stars: ✭ 103 (-0.96%)
Mutual labels:  mock, stub, tests
stub-server
Stub server for REST APIs
Stars: ✭ 14 (-86.54%)
Mutual labels:  mock, stub
Impersonator
Ruby library to record and replay object interactions
Stars: ✭ 100 (-3.85%)
Mutual labels:  mock, stub
Mongodb Memory Server
Spinning up mongod in memory for fast tests. If you run tests in parallel this lib helps to spin up dedicated mongodb servers for every test file in MacOS, *nix, Windows or CI environments (in most cases with zero-config).
Stars: ✭ 1,376 (+1223.08%)
Mutual labels:  mock, tests
Cuckoo
Boilerplate-free mocking framework for Swift!
Stars: ✭ 1,344 (+1192.31%)
Mutual labels:  mock, stub
InstantMock
Create mocks easily in Swift
Stars: ✭ 88 (-15.38%)
Mutual labels:  mock, stub
phake
PHP Mocking Framework
Stars: ✭ 464 (+346.15%)
Mutual labels:  mock, stub
sinon-mongoose
Extend Sinon stubs for Mongoose methods to test chained methods easily
Stars: ✭ 87 (-16.35%)
Mutual labels:  mock, stub
Swiftmockgeneratorforxcode
An Xcode extension (plugin) to generate Swift test doubles automatically.
Stars: ✭ 522 (+401.92%)
Mutual labels:  mock, stub
Ohhttpstubs
Stub your network requests easily! Test your apps with fake network data and custom response time, response code and headers!
Stars: ✭ 4,831 (+4545.19%)
Mutual labels:  mock, stub
Dyson
Node server for dynamic, fake JSON.
Stars: ✭ 814 (+682.69%)
Mutual labels:  mock, stub
mock
Utilities to help mock behavior, spy on function calls, stub methods, and fake time for tests.
Stars: ✭ 31 (-70.19%)
Mutual labels:  mock, stub
node-mock-examples
Examples of tests that mock Node system APIs: fs, http, child_process, timers
Stars: ✭ 38 (-63.46%)
Mutual labels:  mock, stub
Stubmatic
Mock HTTP calls without coding. Designed specially for testing and testers.
Stars: ✭ 118 (+13.46%)
Mutual labels:  mock, stub
FireMock
Mock and stub HTTP requests. Test your apps with fake data and files responses.
Stars: ✭ 25 (-75.96%)
Mutual labels:  mock, stub
DeepfakeHTTP
DeepfakeHTTP is a web server that uses HTTP dumps as a source for responses.
Stars: ✭ 373 (+258.65%)
Mutual labels:  mock, stub
aem-stubs
Tool for providing sample data for AEM applications in a simple and flexible way. Stubbing server on AEM, no separate needed.
Stars: ✭ 40 (-61.54%)
Mutual labels:  mock, stub
Mocha
Mocha is a mocking and stubbing library for Ruby
Stars: ✭ 1,065 (+924.04%)
Mutual labels:  mock, stub
Hippolyte
HTTP Stubbing in Swift
Stars: ✭ 109 (+4.81%)
Mutual labels:  mock, stub
Wiremockui
Wiremock UI - Tool for creating mock servers, proxies servers and proxies servers with the option to save the data traffic from an existing API or Site.
Stars: ✭ 38 (-63.46%)
Mutual labels:  mock, stub

Mimic Build Status Hex pm

A sane way of using mocks in Elixir. It borrows a lot from both Meck & Mox! Thanks @eproxus & @josevalim

Installation

Just add mimic to your list of dependencies in mix.exs:

def deps do
  [
    {:mimic, "~> 1.4", only: :test}
  ]
end

If :applications key is defined inside your mix.exs or you run mix test --no-start, you probably want to add Application.ensure_all_started(:mimic) in your test_helper.exs

Using

Modules need to be prepared so that they can be used.

You must first call copy in your test_helper.exs for each module that may have the behaviour changed.

Mimic.copy(Calculator)

ExUnit.start()

Calling copy will not change the behaviour of the module.

The user must call stub/1, stub/3, expect/4 or reject/1 so that the functions can behave differently.

Then for the actual tests one could use it like this:

use ExUnit.Case, async: true
use Mimic

test "invokes add once and mult twice" do
  Calculator
  |> stub(:add, fn x, y -> :stub end)
  |> expect(:add, fn x, y -> x + y end)
  |> expect(:mult, 2, fn x, y -> x * y end)

  assert Calculator.add(2, 3) == 5
  assert Calculator.mult(2, 3) == 6

  assert Calculator.add(2, 3) == :stub
end

Stub, Expect and Reject

Stub

stub/1 will change every module function to throw an exception if called.

stub(Calculator)

** (Mimic.UnexpectedCallError) Stub! Unexpected call to Calculator.add(3, 7) from #PID<0.187.0>
     code: assert Calculator.add(3, 7) == 10

stub/3 changes a specific function to behave differently. If the function is not called no verification error will happen.

Expect

expect/4 changes a specific function and it works like a queue of operations. It has precedence over stubs and if not called a verification error will be thrown.

If the same function is called with expect/4 the order will be respected:

Calculator
|> stub(:add, fn _x, _y -> :stub end)
|> expect(:add, fn _, _ -> :expected_1 end)
|> expect(:add, fn _, _ -> :expected_2 end)

assert Calculator.add(1, 1) == :expected_1
assert Calculator.add(1, 1) == :expected_2
assert Calculator.add(1, 1) == :stub

expect/4 has an optional parameter which is the amount of calls expected:

Calculator
|> expect(:add, 2, fn x, y -> {:add, x, y} end)

assert Calculator.add(1, 3) == {:add, 1, 3}
assert Calculator.add(4, 5) == {:add, 4, 5}

With use Mimic, verification expect/4 function call of is done automatically on test case end. verify!/1 can be used in case custom verification timing required:

Calculator
|> expect(:add, 2, fn x, y -> {:add, x, y} end)

# Will raise error because Calculator.add is not called
# ** (Mimic.VerificationError) error while verifying mocks for #PID<0.3182.0>:
#   * expected Calculator.add/2 to be invoked 1 time(s) but it has been called 0 time(s)
verify!()

Reject

One may want to reject calls to a specific function. reject/1 can be used to achieved this behaviour.

reject(&Calculator.add/2)
assert_raise Mimic.UnexpectedCallError, fn -> Calculator.add(4, 2) end

Private and Global mode

The default mode is private which means that only the process and explicitly allowed process will see the different behaviour.

Calling allow/2 will permit a different pid to call the stubs and expects from the original process.

If you are using Task there is no need to use global mode as Tasks can see the same expectations and stubs from the calling process.

Global mode can be used with set_mimic_global like this:

setup :set_mimic_global

test "invokes add and mult" do
  Calculator
  |> expect(:add, fn x, y -> x + y end)
  |> expect(:mult, fn x, y -> x * y end)

  parent_pid = self()

  spawn_link(fn ->
    assert Calculator.add(2, 3) == 5
    assert Calculator.mult(2, 3) == 6

    send parent_pid, :ok
  end)

  assert_receive :ok
end

This means that all processes will get the same behaviour defined with expect & stub. This option is simpler but tests running concurrently will have undefined behaviour. It is important to run with async: false. One could use :set_mimic_from_context instead of using :set_mimic_global or :set_mimic_private. It will be private if async: true, global otherwise.

DSL Mode

To use DSL Mode use Mimic.DSL rather than use Mimic in your test. DSL Mode enables a more expressive api to the Mimic functionality.

  use Mimic.DSL

  test "basic example" do
    stub Calculator.add(_x, _y), do: :stub
    expect Calculator.add(x, y), do: x + y
    expect Calculator.mult(x, y), do: x * y

    assert Calculator.add(2, 3) == 5
    assert Calculator.mult(2, 3) == 6

    assert Calculator.add(2, 3) == :stub
  end

Implementation Details & Performance

After calling Mimic.copy(MyModule), calls to functions belonging to this module will first go through an ETS table to check which pid sees what (stubs, expects or call original).

It is really fast but it won't be as fast as calling a no-op function. Here's a very simple benchmark:

defmodule Enumerator do
 def to_list(x, y), do: Enum.to_list(x..y)
end

Benchmarking Enumerator.to_list(1, 100) :

Name               ips        average  deviation         median         99th %
mimic         116.00 K        8.62 μs   ±729.13%           5 μs          29 μs
original       19.55 K       51.15 μs   ±302.46%          34 μs         264 μs

Comparison:
mimic         116.00 K
original       19.55 K - 5.93x slower

Benchmarking Enumerator.to_list(1, 250) :

Name               ips        average  deviation         median         99th %
original      131.49 K        7.61 μs   ±167.90%           7 μs          16 μs
mimic         105.47 K        9.48 μs   ±145.21%           9 μs          27 μs

Comparison:
original      131.49 K
mimic         105.47 K - 1.25x slower

There's a small fixed price to pay when mimic is used but it is unnoticeable for tests purposes.

Acknowledgements

Thanks to @jamesotron and @alissonsales for all the help! 🎉

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].