How to pass Django mock instance to class method?
Question: The Mock testing library is the one Django topic I just can't seem to wrap my
head around. For example, in the following code, why don't the mock User
instances that I create in my unit test appear in the User object that I query
in the 'get_user_ids' method? If I halt the test in the 'get_user_ids' method
via the debug call and do "User.objects.all()", there's nothing in the User
queryset and the test fails. Am I not creating three mock User instances that
will be queried the the UserProxy's static method?

I'm using Django 1.6 and Postgres 9.3 and running the test with the command
"python manage.py test -s apps.profile.tests.model_tests:TestUserProxy".

Thanks!

    
    
    # apps/profile/models.py
    from django.contrib.auth.models import User
    class UserProxy(User):
        class Meta:
            proxy = True
    
        @staticmethod
        def get_user_ids(usernames):
            debug()
            user_ids = []
            for name in usernames:
                try:
                    u = User.objects.get(username__exact=name)
                    user_ids.append(u.id)
                except ObjectDoesNotExist:
                    logger.error("We were unable to find '%s' in a list of usernames." % name)
            return user_ids
    
    
    # apps/profile/tests/model_tests.py
    from django.test import TestCase
    from django.contrib.auth.models import User
    from mock import Mock
    from apps.profile.models import UserProxy
    
    class TestUserProxy(TestCase):
        def test_get_user_ids(self):
            u1 = Mock(spec=User)
            u1.id = 1
            u1.username = 'user1'
    
            u2 = Mock(spec=User)
            u2.id = 2
            u2.username = 'user2'
    
            u3 = Mock(spec=User)
            u3.id = 3
            u3.username = 'user3'
    
            usernames = [u1.username, u2.username, u3.username]
            expected = [u1.id, u2.id, u3.id]
            actual = UserProxy.get_user_ids(usernames)
            self.assertEqual(expected, actual)
    


Answer: Mocking is awesome for testing, and can lead to very clean tests, however it
suffers a little from (a) being a bit fiddly to get ones head around when
starting out, and (b) does require some effort often to set up mock objects
and have then injected/used in the correct places.

The mock objects you are creating for the users are objects that look like a
Django `User` model object, but they are not actual model objects, and
therefore do not get put into the database.

To get your test working, you have two options, depending on what kind of test
you want to write.

**Unit Test - Mock the data returned from the database**

The first option is to get this working as a unit test, i.e. testing the
`get_user_ids` method in isolation from the database layer. To do this, you
would need to mock the call to `User.objects.get(username__exact=name)` so
that it returns the three mock objects you created in your test. This would be
the more correct approach (as it is better to test units of code in
isolation), however it would involve more work to set up than the alternative
below.

One way to achieve this would be to firstly separate out the user lookup into
it's own function in _apps/profile/models.py_ :

    
    
    def get_users_by_name(name):
        return User.objects.get(username__exact=name)
    

This would need to be called in your function, by replacing the call to
`Users.objects.get(username__exact=name)` with `get_users_by_name(name)`. You
can then modify your test to patch the function like so:

    
    
    from django.test import TestCase
    from django.contrib.auth.models import User
    from mock import Mock, patch
    from apps.profile.models import UserProxy
    
    class TestUserProxy(TestCase):
    
        @patch('apps.profile.models.get_user_by_name')
        def test_get_user_ids(self, mock_get_user_by_name):
            u1 = Mock(spec=User)
            u1.id = 1
            u1.username = 'user1'
    
            u2 = Mock(spec=User)
            u2.id = 2
            u2.username = 'user2'
    
            u3 = Mock(spec=User)
            u3.id = 3
            u3.username = 'user3'
    
            # Here is where we wire up the mocking - we take the patched method to return
            # users and tell it that, when it is called, it must return the three mock
            # users you just created.
            mock_get_user_by_name.return_value = [u1, u2, u3]
    
            usernames = [u1.username, u2.username, u3.username]
            expected = [u1.id, u2.id, u3.id]
            actual = UserProxy.get_user_ids(usernames)
            self.assertEqual(expected, actual)
    

**Integration Test - Create real user objects**

The second approach is to modify this to be an integration test, i.e. one that
tests both this unit of code and also the interaction with the database. This
is a little less clean, in that you are now exposing your tests on the method
to the chance of failing because of problems in a different unit of code (i.e.
the Django code that interacts with the database). However, this does make the
setup of the test a lot simpler, and pragmatically may be the right approach
for you.

To do this, simply remove the mocks you have created and create actual users
in the database as part of your test.

