This module aims to be a lightweight and flexible alternative to the popular mock framework.
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. |
Lightweight spies and mock responses
Example usage, suppose you want to test this class:
>>> class ProductionClass(object):
... def method(self):
... return 'stuff'
>>> real = ProductionClass()
With aspectlib.test:
>>> from aspectlib import weave, test
>>> patch = weave(real.method, [test.mock(3), test.record])
>>> real.method(3, 4, 5, key='value')
3
>>> assert real.method.calls == [(real, (3, 4, 5), {'key': 'value'})]
As a bonus, you have an easy way to rollback all the mess:
>>> patch.rollback()
>>> real.method()
'stuff'
With mock:
>>> from mock import Mock
>>> real = ProductionClass()
>>> real.method = Mock(return_value=3)
>>> real.method(3, 4, 5, key='value')
3
>>> real.method.assert_called_with(3, 4, 5, key='value')
Factory or decorator (depending if func is initially given).
Parameters: |
|
---|---|
Returns: | A wrapper that has a calls property. |
The decorator 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.
Elaborate tools for testing difficult code
This a simple yet flexible tool that can do “capture-replay mocking” or “test doubles” [1]. It leverages aspectlib‘s powerful weaver.
Parameters: |
|
---|
The Story allows some testing patterns that are hard to do with other tools:
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 |
Parameters: |
|
---|---|
Returns: | A aspectlib.test.Replay object. |
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('other arg') == 'other result' # returns
+mymod.func('bogus arg') == None # returns
mymod.func('some arg') == 'some result' # returns
Object implementing the replay transaction.
This object should be created by Story‘s replay method.
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.
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.badfunc() ** ValueError('boom!') # raises
mymod.func('some arg') == None # returns
We can just take the output and paste in the story:
>>> import mymod
>>> with Story(mymod) as story:
... mymod.badfunc() ** ValueError('boom!') # raises
... mymod.func('some arg') == None # returns
>>> with story.replay():
... mymod.func('some arg')
... try:
... mymod.badfunc()
... except ValueError as exc:
... print(exc)
boom!