Fork me on GitHub

Reference: aspectlib.test

This module aims to be a lightweight and flexible alternative to the popular mock framework and more.

aspectlib.test.record Factory or decorator (depending if func is initially given).
aspectlib.test.mock Factory for a decorator that makes the function return a given return_value.
aspectlib.test.Story This a simple yet flexible tool that can do “capture-replay mocking” or “test doubles” [1].
aspectlib.test.Replay Object implementing the replay transaction.
aspectlib.test.record(func=None, recurse_lock_factory=<function allocate_lock at 0x7f0113e17668>, **options)[source]

Factory or decorator (depending if func is initially given).

Parameters:
  • callback (list) – An a callable that is to be called with instance, function, args, kwargs.
  • calls (list) – An object where the Call objects are appended. If not given and callback is not specified then a new list object will be created.
  • iscalled (bool) – If True the func will be called. (default: False)
  • extended (bool) – If True the func‘s __name__ will also be included in the call list. (default: False)
  • results (bool) – If True the results (and exceptions) will also be included in the call list. (default: False)
Returns:

A wrapper that records all calls made to func. The history is available as a call property. If access to the function is too hard then you need to specify the history manually.

Example

>>> @record
... def a(x, y, a, b):
...     pass
>>> a(1, 2, 3, b='c')
>>> a.calls
[Call(self=None, args=(1, 2, 3), kwargs={'b': 'c'})]

Or, with your own history list:

>>> calls = []
>>> @record(calls=calls)
... def a(x, y, a, b):
...     pass
>>> a(1, 2, 3, b='c')
>>> a.calls
[Call(self=None, args=(1, 2, 3), kwargs={'b': 'c'})]
>>> calls is a.calls
True

Changed in version 0.9.0: Renamed history option to calls. Renamed call option to iscalled. Added callback option. Added extended option.

aspectlib.test.mock(return_value, call=False)[source]

Factory for a decorator that makes the function return a given return_value.

Parameters:
  • return_value – Value to return from the wrapper.
  • call (bool) – If True, call the decorated function. (default: False)
Returns:

A decorator.

class aspectlib.test.Story(*args, **kwargs)[source]

This a simple yet flexible tool that can do “capture-replay mocking” or “test doubles” [1]. It leverages aspectlib‘s powerful weaver.

Parameters:
  • target (same as for aspectlib.weave) – Targets to weave in the story/replay transactions.
  • subclasses (bool) – If True, subclasses of target are weaved. Only available for classes
  • aliases (bool) – If True, aliases of target are replaced.
  • lazy (bool) – If True only target’s __init__ method is patched, the rest of the methods are patched after __init__ is called. Only available for classes.
  • methods (list or regex or string) – Methods from target to patch. Only available for classes

The Story allows some testing patterns that are hard to do with other tools:

  • Proxied mocks: partially mock objects and modules so they are called normally if the request is unknown.
  • Stubs: completely mock objects and modules. Raise errors if the request is unknown.

The Story works in two of transactions:

  • The story: You describe what calls you want to mocked. Initially you don’t need to write this. Example:

    >>> import mymod
    >>> with Story(mymod) as story:
    ...     mymod.func('some arg') == 'some result'
    ...     mymod.func('bad arg') ** ValueError("can't use this")
    
  • The replay: You run the code uses the interfaces mocked in the story. The replay always starts from a story instance.

Changed in version 0.9.0: Added in.

[1](1, 2) http://www.martinfowler.com/bliki/TestDouble.html
replay(**options)[source]
Parameters:
  • proxy (bool) – If True then unexpected uses are allowed (will use the real functions) but they are collected for later use. Default: True.
  • strict (bool) – If True then an AssertionError is raised when there were unexpected calls or there were missing calls (specified in the story but not called). Default: True.
  • dump (bool) – If True then the unexpected/missing calls will be printed (to sys.stdout). Default: True.
Returns:

obj:aspectlib.test.Replay object.

Return type:

A

Example

>>> import mymod
>>> with Story(mymod) as story:
...     mymod.func('some arg') == 'some result'
...     mymod.func('other arg') == 'other result'
>>> with story.replay(strict=False):
...     print(mymod.func('some arg'))
...     mymod.func('bogus arg')
some result
Got bogus arg in the real code !
STORY/REPLAY DIFF:
    --- expected...
    +++ actual...
    @@ -1,2 +1,2 @@
     mymod.func('some arg') == 'some result'  # returns
    -mymod.func('other arg') == 'other result'  # returns
    +mymod.func('bogus arg') == None  # returns
ACTUAL:
    mymod.func('some arg') == 'some result'  # returns
    mymod.func('bogus arg') == None  # returns
class aspectlib.test.Replay(?)[source]

Object implementing the replay transaction.

This object should be created by Story‘s replay method.

diff[source]

Returns a pretty text representation of the unexpected and missing calls.

Most of the time you don’t need to directly use this. This is useful when you run the replay in strict=False mode and want to do custom assertions.

missing[source]

Returns a pretty text representation of just the missing calls.

unexpected[source]

Returns a pretty text representation of just the unexpected calls.

The output should be usable directly in the story (just copy-paste it). Example:

>>> import mymod
>>> with Story(mymod) as story:
...     pass
>>> with story.replay(strict=False, dump=False) as replay:
...     mymod.func('some arg')
...     try:
...         mymod.badfunc()
...     except ValueError as exc:
...         print(exc)
Got some arg in the real code !
boom!
>>> print(replay.unexpected)
mymod.func('some arg') == None  # returns
mymod.badfunc() ** ValueError('boom!',)  # raises

We can just take the output and paste in the story:

>>> import mymod
>>> with Story(mymod) as story:
...     mymod.func('some arg') == None  # returns
...     mymod.badfunc() ** ValueError('boom!')  # raises
>>> with story.replay():
...     mymod.func('some arg')
...     try:
...         mymod.badfunc()
...     except ValueError as exc:
...         print(exc)
boom!