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(), and fudge.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 the fudge.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 using fudge.Fake.next_call() after expects(...), each new call will be part of the expected order

Declaring expects() multiple times is the same as declaring fudge.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 with fudge.Fake.expects() and fudge.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 declaring fudge.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 constructor

Take 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 use fudge.inspector functions. Here is an example of providing a more flexible with_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 in fudge.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() and fudge.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() and fudge.with_fakes()

fudge.with_fakes(method)

Decorator that calls fudge.clear_calls() before method() and fudge.verify() afterwards.

class fudge.FakeDeclarationError

Exception in how this fudge.Fake was declared.