Mocker

class privex.helpers.collections.Mocker(modules: dict = None, attributes: dict = None, *args, **kwargs)[source]

This mock class is designed to be used either to act as a stand-in “noop” (no operation) object, which could be used either as a drop-in replacement for a failed module / class import, or for certain unit tests.

If you need additional functionality such as methods having actual behaviour, you can set attributes on a Mocker instance to either a lambda, or point them at a real function/method:

>>> m = Mocker()
>>> m.some_func = lambda a: a+1
>>> m.some_func(5)
6

Example use case - fallback for unimportant module imports

Below is a real world example of using Mocker and privex.helpers.decorators.mock_decorator() to simulate pytest - allowing your tests to run under the standard unittest framework if a user doesn’t have pytest (as long as your tests aren’t critically dependent on PyTest).

Try importing pytest then fallback to a mock pytest:

>>> try:
...     import pytest
... except ImportError:
...     from privex.helpers import Mocker, mock_decorator
...     print('Failed to import pytest. Using privex.helpers.Mocker to fake pytest.')
...     # Make pytest pretend to be the class 'module' (the class actually used for modules)
...     pytest = Mocker.make_mock_class('module')
...     # To make pytest.mark.skip work, we add the fake module 'mark', then set skip to `mock_decorator`
...     pytest.add_mock_module('mark')
...     pytest.mark.skip = mock_decorator
...

Since we added the mock module mark, and set the attribute skip to point at mock_decorator, the test function test_something won’t cause a syntax error. mock_decorator will just call test_something() which doesn’t do anything anyway:

>>> @pytest.mark.skip(reason="this test doesn't actually do anything...")
... def test_something():
...     pass
>>>
>>> def test_other_thing():
...     if True:
...         return pytest.skip('cannot test test_other_thing because of an error')
...
>>>

Generating “disguised” mock classes

If you need the mock class to appear to have a certain class name and/or module path, you can generate “disguised” mock classes using make_mock_class() like so:

>>> redis = Mocker.make_mock_class('Redis', module='redis')
>>> redis
<redis.Redis object at 0x7fd7402ea4a8>

A :class:`.Mocker` instance has the following behaviour

  • Attributes that don’t exist result in a function being returned, which accepts any arguments / keyword args, and simply returns None

Example:

>>> m = Mocker()
>>> repr(m.randomattr('hello', world=123))
'None'
  • Arbitrary attributes x.something and items x['something'] can be set on an instance, and they will be similarly returned when they’re accessed. Attributes and items share the same key/value’s, so the following examples are all accessing the same data:

Example:

>>> m = Mocker()
>>> m.example = 'hello'
>>> m['example'] = 'world'
>>> print(m.example)
world
>>> print(m['example'])
world
  • You can add arbitrary “modules” to a Mocker instance. With only the name argument, add_mock_module() will add a “module” under the instance, which is really just another Mocker instance.

Example:

>>> m = Mocker()
>>> m.add_mock_module('my_module')
>>> m.my_module.example = 'hello'
>>> print(m.my_module['example'], m.my_module.example)
hello hello
__init__(modules: dict = None, attributes: dict = None, *args, **kwargs)[source]

Initialize self. See help(type(self)) for accurate signature.

Methods

Methods

__init__([modules, attributes])

Initialize self.

add_mock_module(name[, value, mock_attrs, …])

Add a fake sub-module to this Mocker instance.

make_mock_class([name, instance, simple, …])

Return a customized mock class or create an instance which appears to be named name