The 90% use case is that want to stub out a side effect. This is also known as (monkey-)patching. With mockito, it's:
from mockito import when # stub `os.path.exists` when(os.path).exists('/foo').thenReturn(True) os.path.exists('/foo') # => True os.path.exists('/bar') # -> throws unexpected invocation
So in difference to traditional patching, in mockito you always specify concrete arguments (a call signature), and its outcome, usually a return value via thenReturn or a raised exception via thenRaise. That effectively turns function calls into constants for the time of the test.
There are of course reasons when you don't want to overspecify specific tests. You just want the desired stub answer. Here we go:
when(os.path).exists(...).thenReturn(True) # now, obviously, you get the same answer, regardless of the arguments os.path.exists('FooBar') # => True os.path.exists('BarFoo') # => True
You can combine both stubs. E.g. nothing exists, except one file:
when(os.path).exists(...).thenReturn(False) when(os.path).exists('.flake8').thenReturn(True)
And because it's a similar pattern, we can introduce :func:`spy2` here. Spies call through the original implementation of a given function. E.g. everything is as it is, except '.flake8' is just not there:
from mockito import spy2 spy2(os.path.exists) when(os.path).exists('.flake8').thenReturn(False)
Another way to write the same thing is:
when(os.path).exists(...).thenCallOriginalImplementation() when(os.path).exists('.flake8').thenReturn(False)
And actually spy2 uses thenCallOriginalImplementation under the hood. Why spying at all: either you want the implementation almost intact as above or you need the implementation to stay intact but want to verify its usage. E.g.:
spy2(os.path.exists) # now do some stuff do_stuff() # then verify the we asked for the cache exactly once verify(os.path, times=1).exists("cache/.foo")
When patching, you MUST not forget to :func:`unstub` of course! You can do this explicitly
from mockito import unstub unstub() # restore os.path module
Usually you do this unconditionally in your teardown function. If you're using pytest, you could define a fixture instead
# conftest.py import pytest @pytest.fixture def unstub(): from mockito import unstub yield unstub() # my_test.py import pytest pytestmark = pytest.mark.usefixtures("unstub")
But very often you just use context managers (aka with), and mockito will unstub on 'exit' automatically:
# E.g. test that `exists` gets never called with expect(os.path, times=0).exists('.flake8'): # within the block `os.path.exists` is patched cached_dir_lookup('.flake8') # at the end of the block `os.path` gets unpatched # Which is btw roughly the same as doing with when(os.path).exists('.flake8'): cached_dir_lookup('.flake8') verify(os.path, times=0).exists(...)
Now let's mix global module patching with mocks. We want to test the following function using the fab requests library:
import requests def get_text(url): res = requests.get(url) if 200 <= res.status_code < 300: return res.text return None
How, dare, we did not inject our dependencies! Obviously we can get over that by patching at the module level like before:
when(requests).get('https://example.com/api').thenReturn(...)
But what should we return? We know it's a requests.Response object, (Actually I know this bc I typed this in the ipython REPL first.) But how to construct such a Response, its __init__ doesn't even take any arguments?
Should we actually use a 'real' response object? No, we fake it using :func:`mock`.
# setup response = mock({ 'status_code': 200, 'text': 'Ok' }, spec=requests.Response) when(requests).get('https://example.com/api').thenReturn(response) # run assert get_text('https://example.com/api') == 'Ok' # done!
Say you want to mock the class Dog:
class Dog(object): def bark(self): return 'Wuff' # either mock the class when(Dog).bark().thenReturn('Miau!') # now all instances have a different behavior rex = Dog() assert rex.bark() == 'Miau!' # or mock a concrete instance when(rex).bark().thenReturn('Grrrr') assert rex.bark() == 'Grrrr' # a different dog will still 'Miau!' assert Dog().bark() == 'Miau!' # be sure to call unstub() once in while unstub()
Sure, you can verify your interactions:
from mockito import verify # once again rex = Dog() when(rex).bark().thenReturn('Grrrr') rex.bark() rex.bark() # `times` defaults to 1 verify(rex, times=2).bark()
In general mockito is very picky:
# this will fail because `Dog` has no method named `waggle` when(rex).waggle().thenReturn('Nope') # this will fail because `bark` does not take any arguments when(rex).bark('Grrr').thenReturn('Nope') # given this function def bark(sound, post='!'): return sound + post from mockito import kwargs when(main).bark('Grrr', **kwargs).thenReturn('Nope') # now this one will fail bark('Grrr') # because there are no keyword arguments used # this one will fail because `then` does not match the function signature bark('Grrr', then='!!') # this one will go bark('Grrr', post='?') # there is also an args matcher def add_tasks(*tasks, verbose=False): pass from mockito import args # If you omit the `thenReturn` it will just return `None` when(main).add_tasks(*args) add_tasks('task1', 'task2') # will go add_tasks() # will fail add_tasks('task1', verbose=True) # will fail too # On Python 3 you can also use `...` when(main).add_tasks(...) # when(main).add_tasks(Ellipsis) on Python 2 add_tasks('task1') # will go add_tasks(verbose=True) # will go add_tasks('task1', verbose=True) # will go add_tasks() # will go
To start with an empty stub use :func:`mock`:
from mockito import mock obj = mock() # pass it around, eventually it will be used obj.say('Hi') # back in the tests, verify the interactions verify(obj).say('Hi') # by default all invoked methods take any arguments and return None # you can configure your expected method calls with the usual `when` when(obj).say('Hi').thenReturn('Ho') # There is also a shortcut to set some attributes obj = mock({ 'hi': 'ho' }) assert obj.hi == 'ho' # This would work for methods as well; in this case obj = mock({ 'say': lambda _: 'Ho' }) # But you don't have any argument and signature matching assert obj.say('Anything') == 'Ho' # At least you can verify your calls verify(obj).say(...) # Btw, you can make screaming strict mocks:: obj = mock(strict=True) # every unconfigured, unexpected call will raise
You can use an empty stub specced against a concrete class:
# Given the above `Dog` rex = mock(Dog) # Now you can stub out any known method on `Dog` but other will throw when(rex).bark().thenReturn('Miau') # this one will fail when(rex).waggle() # These mocks are in general very strict, so even this will fail rex.health # unconfigured attribute # Of course you can just set it in a setup routine rex.health = 121 # Or again preconfigure rex = mock({'health': 121}, spec=Dog) # preconfigure stubbed method rex = mock({'bark': lambda sound: 'Miau'}, spec=Dog) # as you specced the mock, you get at least function signature matching # `bark` does not take any arguments so rex.bark('sound') # will throw TypeError # Btw, you can make loose specced mocks:: rex = mock(Dog, strict=False)