The Python Oracle

Django - Mocking the save method on a model

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Techno Bleepage Open

--

Chapters
00:00 Django - Mocking The Save Method On A Model
01:13 Accepted Answer Score 13
01:47 Answer 2 Score 0
01:58 Answer 3 Score 0
03:13 Thank you

--

Full question
https://stackoverflow.com/questions/2766...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #django #unittesting #mocking

#avk47



ACCEPTED ANSWER

Score 13


After some more googling, I found something that works :

@mock.patch.object(models.KangaUser, 'save')
@mock.patch('django.contrib.auth.models.User')
def test_create(self, user_class_mock, kangauser_save_mock):
    # Mocking configuration
    # User
    user_class_mock.objects = mock.MagicMock()
    user_class_mock.objects.create_user = mock.MagicMock()
    user_class_mock.objects.create_user.return_value = User(id=1)

    # Test
    kangauser_manager = models.KangaUserManager()
    kanga_user = kangauser_manager.create(self.username, self.email, self.password, self.last_name, self.first_name, self.request, self.registered, self.send_confirmation)

    # Checks
    # create_user called with good parameters
    user_class_mock.objects.create_user.assert_called_with(username=self.username, email=self.email, password=self.password, first_name=self.first_name, last_name=self.last_name)
    # KangaUser 
    self.assertTrue(kangauser_save_mock.called)

So, the trick was to use @mock.patch.object !




ANSWER 2

Score 0


With pytest-mock you can do:

def test_create(self, mocker):
    mocker.patch("path_to_the_kanga_user_manager.KangaUser.save")




ANSWER 3

Score 0


@CBrunain's answer got me started on the right path, particularly with mock.patch.object(models.KangaUser, 'save'). In my case, however, my unit test still needed Model.save to function how it's supposed to.

I ended up finding the wraps parameter:

wraps: Item for the mock object to wrap. If wraps is not None then calling the Mock will pass the call through to the wrapped object (returning the real result). Attribute access on the mock will return a Mock object that wraps the corresponding attribute of the wrapped object (so attempting to access an attribute that doesn’t exist will raise an AttributeError).

In other words, you can mock save without overriding its functionality just for the purpose of checking how many times it's been called: @mock.patch.object(models.KangaUser, 'save', wraps=models.KangaUser.save)

To give the full answer, the code below is the same as @CBrunain's but with the adjusted mock and an additional Mock.call_count example:

@mock.patch.object(models.KangaUser, 'save', wraps=models.KangaUser.save)
@mock.patch('django.contrib.auth.models.User')
def test_create(self, user_class_mock, kangauser_save_mock):
    # Mocking configuration
    # User
    user_class_mock.objects = mock.MagicMock()
    user_class_mock.objects.create_user = mock.MagicMock()
    user_class_mock.objects.create_user.return_value = User(id=1)

    # Test
    kangauser_manager = models.KangaUserManager()
    kanga_user = kangauser_manager.create(self.username, self.email, self.password, self.last_name, self.first_name, self.request, self.registered, self.send_confirmation)

    # Checks
    # create_user called with good parameters
    user_class_mock.objects.create_user.assert_called_with(username=self.username, email=self.email, password=self.password, first_name=self.first_name, last_name=self.last_name)
    # KangaUser 
    self.assertTrue(kangauser_save_mock.called)

    self.assertEqual(1, kangauser_save_mock.call_count)