[TIP] Should I always provide a return value when writing my test using mock library?

Carl Meyer carl at oddbird.net
Thu Apr 12 08:35:08 PDT 2012


Hi John,

On 04/10/2012 01:52 AM, John Yeukhon Wong wrote:
> Suppose I have a function called "render_reverse" which takes two
> arguments: function name, and args list, and it returns "reverse(f, *args)"
> 
> If I called render_reverse('happy_birthday', {'args': [username]}), I
> would get this:   /greeting/birthday/username/
> 
> I am going to patch the `reverse` function that's local to
> render_reverse, and inside the test, should I always provide a return
> value like this?
> 
>     with patch('myproject.myapps.mylibrary.reverse') as mock_reverse:
>          mock_f = MagicMock(name='f')
>          mock_kwargs = MagicMock(name='kwargs')
>          mock_reverse.return_value = ' /greeting/birthday/johnsmith/'
>          response = mock_reverse(mock_f, mock_kwargs)
> 
>     self.assert......
> 
> 
> What is the best practice in general? How do I determine whether I want
> to provide a return value or not? In almost any cases, how do I know
> things go well? Sometimes I can't differentiate unittest from
> integration / system test (I want to see other codes ikn the same
> function execute and throw back the right result!!!!)

The first thing is to be clear in your mind which code you are intending
to test. In this case, it sounds like you want to test your
"render_reverse" function, but without exercising Django's "reverse"
(which makes it a unit test). Mocking "reverse" is a reasonable way to
go about this.

The next question is what behavior you want to assert about your code
under test. If you want to assert that your function returns whatever is
returned by "reverse", you could write a test like this:

    with patch("myproject.myapps.mylibrary.reverse") as mock_reverse:
         mock_reverse.return_value = "/greeting/bday/johnsmith/"
         result = render_reverse("happy_bday", {"args": ["username"]})

    self.assertEqual(result, "/greeting/bday/johnsmith")

If you want to assert that "render_reverse" passes on its arguments to
"reverse" as you described (taking "args" from the dictionary and
passing it on as *args), you could write a test like this:

    with patch("myproject.myapps.mylibrary.reverse") as mock_reverse:
         mock_reverse.return_value = "/greeting/bday/johnsmith/"
         render_reverse("happy_bday", {"args": ["username"]})

    mock_reverse.assert_called_with("happy_bday", "username")

(Note that setting the return value of mock_reverse is only necessary
for this second test if your render_reverse function does something with
that return value that would barf if it got a Mock instance.)

You could also combine these into a single test with two assertions, but
in general that gets you less useful test failures.

There are two things your sample code does that don't make sense in any
case: (1) Constructing MagicMock instances to pass in as the arguments,
and (2) Calling mock_reverse in the final line, rather than calling the
render_reverse function that you are wanting to test.

Hope this helps!

Carl

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: OpenPGP digital signature
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20120412/212025bc/attachment.pgp>


More information about the testing-in-python mailing list