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

Eric Larson eric at ionrock.org
Thu Apr 12 12:46:03 PDT 2012


On Thursday, April 12, 2012 at 2:18 PM, John Wong wrote:
> First, I hope I am not making duplicate (my first time using this kind of mailing list...)
>  
> > 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.
>  
>  
> Yes. Exactly. I want to perform a unittest on this function, which calls django's reverse (for whatever reason why I am doing this...)
>  
> Here is the actual function from lib.py (there is no class involved, all functions in lib.py)
>  
> from django.core.urlresolvers import reverse
> def render_reverse(f, kwargs):
> """
> kwargs is a dictionary, usually of the form {'args': [cbid]}
> """
> return reverse(f, **kwargs)
>  
> Because the name lookup is `reverse`, so I should patch `myproject.myapps.mylibrary.reverse` as opposed to `django.core.urlresolvers.reverse`
>  
> I did the first method. I want to assert that `render_reverse` generates whatever `reverse` generates (by calling `reverse` directly). That's the goal. So I have the following test code:
>  
> from lib import render_reverse, print_ls
>  
> class LibTest(unittest.TestCase):
>  
> def test_render_reverse_is_correct(self):
> with patch('myproject.myapps.mylibrary.reverse') as mock_reverse:
> from lib import render_reverse
> mock_f = MagicMock(name='f', return_value='dummy_views')
> mock_kwargs = MagicMock(name='kwargs',return_value={'args':['123']})
> mock_reverse.return_value = '/natrium/cb/details/123'
> response = render_reverse(mock_f(), mock_kwargs())
> print mock_reverse.mock_calls # prints []
> print mock_reverse.mock_calls # prints []
> self.assertTrue('/natrium/cb/details/' in response)
>  

I might consider rewriting the test like this:

import lib
import mock

@mock.patch.object(lib, 'reverse')
def test_render_reverse(mock_reverse):
    result = lib.render_reverse('foo', {'args': ['123']})
    mock_reverse.assert_called_with('foo', args='123')
    assert result == mock_reverse('foo', args='123')

The assertion of the result might be pointless, but it feels like it confirms what the code under test should produce. Also, I realize I dropped the "dummy_views" from the argument simply b/c it seems superfluous in my example. Obviously, if it necessary, then it is necessary and should be in the test… right?

If others have comments on my refactoring please let me know. I'm new to the mock library and have been in the process of updating a rather old and unorganized test suite, so tips, tricks and best practices are all appreciated.

Thanks!

Eric  
>  
> Observations:
> 1. mock_reverse was never because `mock_calls` returns []. I've asserted with `called` method, and it returns False.  
> 2. using render_reverse like this, the name `dummy_views` must exists - it must be a view function that actually exists in my Django project. Otherwise, it will return an error.
> 3. using render_reverse like this, the speed takes 0.2 - 0.3 seconds, as opposed to 0.06 s when I used `mock_reverse`.  
>  
> Is there anything wrong the code? I understand that `mock_f` and `mock_kwargs` should be just simple input, rather than mock objects.  
>  
> Thanks, Carl.
> _______________________________________________
> testing-in-python mailing list
> testing-in-python at lists.idyll.org (mailto:testing-in-python at lists.idyll.org)
> http://lists.idyll.org/listinfo/testing-in-python






More information about the testing-in-python mailing list