fudge¶
Fudge is a module for replacing real objects with fakes (mocks, stubs, etc) while testing.
See Using Fudge for common scenarios.
- fudge.patch(*obj_paths)¶
A test decorator that patches importable names with
fakes
Each fake is exposed as an argument to the test:
>>> @fudge.patch('os.remove') ... def test(fake_remove): ... fake_remove.expects_call() ... # do stuff... ... >>> test() Traceback (most recent call last): ... AssertionError: fake:os.remove() was not called
Many paths can be patched at once:
>>> @fudge.patch('os.remove', ... 'shutil.rmtree') ... def test(fake_remove, fake_rmtree): ... fake_remove.is_callable() ... # do stuff... ... >>> test()
For convenience, the patch method calls
fudge.clear_calls()
,fudge.verify()
, andfudge.clear_expectations()
. For that reason, you must manage all your fake objects within the test function itself.Note
If you are using a unittest class, you cannot declare fakes within
setUp()
unless you manually clear calls and clear expectations. If you do that, you’ll want to use thefudge.with_fakes()
decorator instead of@patch
.
- fudge.test(method)¶
Decorator for a test that uses fakes directly (not patched).
Most of the time you probably want to use
fudge.patch()
instead.>>> @fudge.test ... def test(): ... db = fudge.Fake('db').expects('connect') ... # do stuff... ... >>> test() Traceback (most recent call last): ... AssertionError: fake:db.connect() was not called
- class fudge.Fake(name=None, allows_any_call=False, callable=False, expect_call=False)¶
A fake object that replaces a real one while testing.
Most calls with a few exceptions return
self
so that you can chain them together to create readable code.Instance methods will raise either AssertionError or
fudge.FakeDeclarationError
Keyword arguments:
- name=None
Name of the class, module, or function you mean to replace. If not specified, Fake() will try to guess the name by inspecting the calling frame (if possible).
- allows_any_call=False
This is deprecated. Use
Fake:is_a_stub()
instead.- callable=False
This is deprecated. Use
Fake.is_callable()
instead.- expect_call=True
This is deprecated. Use
Fake.expects_call()
instead.
- calls(call)¶
Redefine a call.
The fake method will execute your function. I.E.:
>>> f = Fake().provides('hello').calls(lambda: 'Why, hello there') >>> f.hello() 'Why, hello there'
- expects(call_name)¶
Expect a call.
If the method call_name is never called, then raise an error. I.E.:
>>> session = Fake('session').expects('open').expects('close') >>> session.open() >>> fudge.verify() Traceback (most recent call last): ... AssertionError: fake:session.close() was not called
Note
If you want to also verify the order these calls are made in, use
fudge.Fake.remember_order()
. When usingfudge.Fake.next_call()
afterexpects(...)
, each new call will be part of the expected orderDeclaring
expects()
multiple times is the same as declaringfudge.Fake.next_call()
- expects_call()¶
The fake must be called.
This is useful for when you stub out a function as opposed to a class. For example:
>>> import fudge >>> remove = fudge.Fake('os.remove').expects_call() >>> fudge.verify() Traceback (most recent call last): ... AssertionError: fake:os.remove() was not called
- has_attr(**attributes)¶
Sets available attributes.
I.E.:
>>> User = Fake('User').provides('__init__').has_attr(name='Harry') >>> user = User() >>> user.name 'Harry'
- has_property(**properties)¶
Sets available properties.
I.E.:
>>> mock_name = Fake().is_callable().returns('Jim Bob') >>> mock_age = Fake().is_callable().raises(AttributeError('DOB not set')) >>> user = Fake('User').has_property(name=mock_name, age=mock_age) >>> user.name 'Jim Bob' >>> user.age Traceback (most recent call last): ... AttributeError: DOB not set
- is_a_stub()¶
Turns this fake into a stub.
When a stub, any method is allowed to be called on the Fake() instance and any attribute can be accessed. When an unknown attribute or call is made, a new Fake() is returned. You can of course override any of this with
Fake.expects()
and the other methods.
- is_callable()¶
The fake can be called.
This is useful for when you stub out a function as opposed to a class. For example:
>>> import fudge >>> remove = Fake('os.remove').is_callable() >>> remove('some/path')
- next_call(for_method=None)¶
Start expecting or providing multiple calls.
Note
next_call() cannot be used in combination with
fudge.Fake.times_called()
Up until calling this method, calls are infinite.
For example, before next_call() …
>>> from fudge import Fake >>> f = Fake().provides('status').returns('Awake!') >>> f.status() 'Awake!' >>> f.status() 'Awake!'
After next_call() …
>>> from fudge import Fake >>> f = Fake().provides('status').returns('Awake!') >>> f = f.next_call().returns('Asleep') >>> f = f.next_call().returns('Dreaming') >>> f.status() 'Awake!' >>> f.status() 'Asleep' >>> f.status() 'Dreaming' >>> f.status() Traceback (most recent call last): ... AssertionError: This attribute of fake:unnamed can only be called 3 time(s). Call reset() if necessary or fudge.clear_calls().
If you need to affect the next call of something other than the last declared call, use
next_call(for_method="other_call")
. Here is an example using getters and setters on a session object>>> from fudge import Fake >>> sess = Fake('session').provides('get_count').returns(1) >>> sess = sess.provides('set_count').with_args(5)
Now go back and adjust return values for get_count()
>>> sess = sess.next_call(for_method='get_count').returns(5)
This allows these calls to be made
>>> sess.get_count() 1 >>> sess.set_count(5) >>> sess.get_count() 5
When using
fudge.Fake.remember_order()
in combination withfudge.Fake.expects()
andfudge.Fake.next_call()
each new call will be part of the expected order.
- provides(call_name)¶
Provide a call.
The call acts as a stub – no error is raised if it is not called.:
>>> session = Fake('session').provides('open').provides('close') >>> import fudge >>> fudge.clear_expectations() # from any previously declared fakes >>> fudge.clear_calls() >>> session.open() >>> fudge.verify() # close() not called but no error
Declaring
provides()
multiple times is the same as declaringfudge.Fake.next_call()
- raises(exc)¶
Set last call to raise an exception class or instance.
For example:
>>> import fudge >>> db = fudge.Fake('db').provides('insert').raises(ValueError("not enough parameters for insert")) >>> db.insert() Traceback (most recent call last): ... ValueError: not enough parameters for insert
- remember_order()¶
Verify that subsequent
fudge.Fake.expects()
are called in the right order.For example:
>>> import fudge >>> db = fudge.Fake('db').remember_order().expects('insert').expects('update') >>> db.update() Traceback (most recent call last): ... AssertionError: Call #1 was fake:db.update(); Expected: #1 fake:db.insert(), #2 fake:db.update(), end >>> fudge.clear_expectations()
When declaring multiple calls using
fudge.Fake.next_call()
, each subsequent call will be added to the expected order of calls>>> import fudge >>> sess = fudge.Fake("session").remember_order().expects("get_id").returns(1) >>> sess = sess.expects("set_id").with_args(5) >>> sess = sess.next_call(for_method="get_id").returns(5)
Multiple calls to
get_id()
are now expected>>> sess.get_id() 1 >>> sess.set_id(5) >>> sess.get_id() 5 >>> fudge.verify() >>> fudge.clear_expectations()
- returns(val)¶
Set the last call to return a value.
Set a static value to return when a method is called. I.E.:
>>> f = Fake().provides('get_number').returns(64) >>> f.get_number() 64
- returns_fake(*args, **kwargs)¶
Set the last call to return a new
fudge.Fake
.Any given arguments are passed to the
fudge.Fake
constructorTake note that this is different from the cascading nature of other methods. This will return an instance of the new Fake, not self, so you should be careful to store its return value in a new variable.
I.E.:
>>> session = Fake('session') >>> query = session.provides('query').returns_fake(name="Query") >>> assert query is not session >>> query = query.provides('one').returns(['object']) >>> session.query().one() ['object']
- times_called(n)¶
Set the number of times an object can be called.
When working with provided calls, you’ll only see an error if the expected call count is exceeded
>>> auth = Fake('auth').provides('login').times_called(1) >>> auth.login() >>> auth.login() Traceback (most recent call last): ... AssertionError: fake:auth.login() was called 2 time(s). Expected 1.
When working with expected calls, you’ll see an error if the call count is never met
>>> import fudge >>> auth = fudge.Fake('auth').expects('login').times_called(2) >>> auth.login() >>> fudge.verify() Traceback (most recent call last): ... AssertionError: fake:auth.login() was called 1 time(s). Expected 2.
Note
This cannot be used in combination with
fudge.Fake.next_call()
- with_arg_count(count)¶
Set the last call to expect an exact argument count.
I.E.:
>>> auth = Fake('auth').provides('login').with_arg_count(2) >>> auth.login('joe_user') # forgot password Traceback (most recent call last): ... AssertionError: fake:auth.login() was called with 1 arg(s) but expected 2
- with_args(*args, **kwargs)¶
Set the last call to expect specific argument values.
The app under test must send all declared arguments and keyword arguments otherwise your test will raise an AssertionError. For example:
>>> import fudge >>> counter = fudge.Fake('counter').expects('increment').with_args(25, table='hits') >>> counter.increment(24, table='clicks') Traceback (most recent call last): ... AssertionError: fake:counter.increment(25, table='hits') was called unexpectedly with args (24, table='clicks')
If you need to work with dynamic argument values consider using
fudge.Fake.with_matching_args()
to make looser declarations. You can also usefudge.inspector
functions. Here is an example of providing a more flexiblewith_args()
declaration using inspectors:>>> import fudge >>> from fudge.inspector import arg >>> counter = fudge.Fake('counter') >>> counter = counter.expects('increment').with_args( ... arg.any(), ... table=arg.endswith("hits")) ...
The above declaration would allow you to call counter like this:
>>> counter.increment(999, table="image_hits") >>> fudge.verify()
Or like this:
>>> counter.increment(22, table="user_profile_hits") >>> fudge.verify()
- with_kwarg_count(count)¶
Set the last call to expect an exact count of keyword arguments.
I.E.:
>>> auth = Fake('auth').provides('login').with_kwarg_count(2) >>> auth.login(username='joe') # forgot password= Traceback (most recent call last): ... AssertionError: fake:auth.login() was called with 1 keyword arg(s) but expected 2
- with_matching_args(*args, **kwargs)¶
Set the last call to expect specific argument values if those arguments exist.
Unlike
fudge.Fake.with_args()
use this if you want to only declare expectations about matching arguments. Any unknown keyword arguments used by the app under test will be allowed.For example, you can declare positional arguments but ignore keyword arguments:
>>> import fudge >>> db = fudge.Fake('db').expects('transaction').with_matching_args('insert')
With this declaration, any keyword argument is allowed:
>>> db.transaction('insert', isolation_level='lock') >>> db.transaction('insert', isolation_level='shared') >>> db.transaction('insert', retry_on_error=True)
Note
you may get more mileage out of
fudge.inspector
functions as described infudge.Fake.with_args()
- without_args(*args, **kwargs)¶
Set the last call to expect that certain arguments will not exist.
This is the opposite of
fudge.Fake.with_matching_args()
. It will fail if any of the arguments are passed.>>> import fudge >>> query = fudge.Fake('query').expects_call().without_args( ... 'http://example.com', name="Steve" ... ) >>> query('http://python.org', name="Joe") >>> query('http://example.com') Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with arg http://example.com >>> query("Joe", "Frank", "Bartholomew", "Steve") >>> query(name='Steve') Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with kwarg name=Steve >>> query('http://python.org', name='Steve') Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with kwarg name=Steve >>> query(city='Chicago', name='Steve') Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with kwarg name=Steve >>> query.expects_call().without_args('http://example2.com') fake:query >>> query('foobar') >>> query('foobar', 'http://example2.com') Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with arg http://example2.com >>> query.expects_call().without_args(name="Hieronymus") fake:query >>> query("Gottfried", "Hieronymus") >>> query(name="Wexter", other_name="Hieronymus") >>> query('asdf', name="Hieronymus") Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with kwarg name=Hieronymus >>> query(name="Hieronymus") Traceback (most recent call last): ... AssertionError: fake:query() was called unexpectedly with kwarg name=Hieronymus >>> query = fudge.Fake('query').expects_call().without_args( ... 'http://example.com', name="Steve" ... ).with_args('dog') >>> query('dog') >>> query('dog', 'http://example.com') Traceback (most recent call last): ... AssertionError: fake:query('dog') was called unexpectedly with args ('dog', 'http://example.com') >>> query() Traceback (most recent call last): ... AssertionError: fake:query('dog') was called unexpectedly with args ()
- fudge.clear_calls()¶
Begin a new set of calls on fake objects.
Specifically, clear out any calls that were made on previously registered fake objects and reset all call stacks. You should call this any time you begin making calls on fake objects.
This is also available in
fudge.patch()
,fudge.test()
andfudge.with_fakes()
- fudge.verify()¶
Verify that all methods have been called as expected.
Specifically, analyze all registered fake objects and raise an AssertionError if an expected call was never made to one or more objects.
This is also available in
fudge.patch()
,fudge.test()
andfudge.with_fakes()
- fudge.with_fakes(method)¶
Decorator that calls
fudge.clear_calls()
before method() andfudge.verify()
afterwards.
- class fudge.FakeDeclarationError¶
Exception in how this
fudge.Fake
was declared.