# 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.