Modern mocking library for Python.
To install mimid
, simply use pip
:
$ pip install mimid
from mimid import mock, every, verify, any, gt
def add(a: int, b: int) -> int:
return a + b
def test_add():
add_mock = mock(add)
every(add_mock).returns(5)
result = add_mock(2, 2)
assert result == 5
verify(add_mock).with_args(any(), gt(0)).called(times=1)
Mimid supports following features:
- easy mock behaviour configuration and verification
- works with classes and plain functions
- fully type hinted - it works with IDE's and type checkers
- clean API - not too much magic
Python built-in mock
module is an awesome tool. It's a first choice if you want to mock something in your tests.
However it has a few disadvantages:
- it doesn't work well with modern IDEs (e.g. auto completion) and type checkers
- it's difficult to define different behaviours for different cases
- it allows too much freedom, you can do anything with your mock object, even if you didn't define any behaviour
Mimid is highly inspired by mocking frameworks from a JVM world, like mockito or mockk.
There are 3 simple steps in the mimid
mocking workflow:
Additionally you can use matchers in both configuration and verification steps.
You have to use mock
function to create your mock object. It works both with classes and functions.
Class example:
from mimid import mock
class A:
def foo(self, param):
pass
class_mock = mock(A)
Function example:
from mimid import mock
def foo(param):
pass
function_mock = mock(foo)
Before you call your mock (function or method) you have to configure its behaviour. Use every
with additional
methods (returns
, raises
, ...) to define how it should works during your test.
from mimid import mock, every
def foo(param):
pass
function_mock = mock(foo)
every(function_mock).returns(1)
You can also specify arguments which should trigger defined behaviour.
from mimid import mock, every
def foo(param):
pass
function_mock = mock(foo)
every(function_mock).with_args(param=2).returns(1)
every(function_mock).with_args(param=3).raises(Exception())
If you want to define property behaviour you have to use prop
function:
from mimid import mock, every, prop
class A:
@property
def x(self) -> int:
pass
class_mock = mock(A)
every(prop(class_mock).x).returns(1)
Available configurations:
Configuration | Description |
---|---|
returns |
return given value |
returns_many |
return each value from provided list |
raises |
raise given exception |
execute |
call given callable |
At the end of your test you can check if mock was called as expected with verify
.
from mimid import mock, verify
def foo(param):
pass
function_mock = mock(foo)
... # mock calls
verify(function_mock).called(times=2)
You can use the same with_args
also during verification step:
from mimid import mock, verify
def foo(param):
pass
function_mock = mock(foo)
... # mock calls
verify(function_mock).with_args(param=1).called(times=2)
You can use matchers during configuration (with_args
) and verification (with_args
, called
) steps. You can also combine matchers with |
or &
and negate it with ~
.
Example:
from mimid import mock, every, verify, gt, lt, gte
def foo(param):
pass
function_mock = mock(foo)
every(function_mock).with_args(gt(0)).returns(1)
result = function_mock(10)
assert result == 1
verify(function_mock).with_args(gt(5) | lt(15)).called(times=gte(1))
capture
is a special matcher - it behaves like any()
but additionally it
stores given argument in provided slot.
Example:
from mimid import mock, every, slot, capture
def foo(param):
pass
function_mock = mock(foo)
param_slot = slot()
every(function_mock).with_args(capture(param_slot)).execute(lambda: param_slot.value + 1)
result = function_mock(1)
assert result == 2
assert param_slot.value == 1
Available matchers:
Matcher | Description |
---|---|
any |
match any value |
eq |
match equal value |
lt |
match lower value |
lte |
match lower or equal value |
gt |
match greater value |
gte |
match greater or equal value |
capture |
capture provided argument |
Created by Konrad Hałas.