A decorator for easily mocking out multiple dependencies by monkey-patching.
@simian.patch(module=..., external=[...], internal=[...])
Installation:
$ pip install simian
simian.patch
is a convenience wrapper around mock.patch with the
following benefits:
- All patched objects are collected under a single
master_mock
, which is provided to the function being decorated. Any patched target can be accessed by its basename (e.g., a patchedtime.sleep
would be accessed in the decorated function asmaster_mock.sleep
). - The patching works even if the target is imported directly (e.g., a call to
sleep
is patched the same as a call totime.sleep
). Simian handles this by reloading the module under test after applying theexternal
patches. - Objects
internal
to the module under test can be patched as well. They are collected under the samemaster_mock
and can be referenced in the same way.
After leaving the decorated function, simian reloads the module under test again, bringing it back to its pre-patched state (although all of the module's addresses in memory have changed).
#
# my_package.my_module
#
from time import sleep
def my_sleep(duration_secs):
print('Sleeping for {n} seconds'.format(n=duration_secs))
sleep(duration_secs)
#
# my_package.test.test_my_module
#
import simian
from my_package import my_module
@simian.patch(my_module, external=['time.sleep'])
def test_my_sleep(master_mock):
my_module.my_sleep(99)
master_mock.sleep.assert_called_once_with(99)
The above example demonstrates external
patching, but internal
(same-module) patching works as well. Let's extend the above example.
#
# my_package.my_module
#
from time import sleep
def my_sleep(duration_secs):
my_logger('Starting {n}-second sleep'.format(n=duration_secs))
sleep(duration_secs)
my_logger('Finished {n}-second sleep'.format(n=duration_secs))
def my_logger(msg):
print(msg)
#
# my_package.test.test_my_module
#
import simian
from mock import call
from my_package import my_module
@simian.patch(my_module, external=['time.sleep'], internal=['my_logger'])
def test_my_sleep(master_mock):
my_module.my_sleep(99)
master_mock.assert_has_calls(
calls=[
call.my_logger('Starting 99-second sleep'),
call.sleep(99),
call.my_logger('Finished 99-second sleep')],
any_order=False)