Reputation: 377
I'm still here with my beginner questions about Django unit tests ^^
I'm trying to test that the save function of a model was called, without actually call the save.
Here is the method i want to test :
from django.db import models
from django.contrib.auth import models as auth_model
from allauth.socialaccount import models as allauth_model
class KangaUserManager(models.Manager):
def create(self, username, email, password, last_name, first_name, request, registered=True, send_confirmation=True):
kanga_user = KangaUser()
kanga_user.user = auth_model.User.objects.create_user(
username=username,
email=email,
password=password,
first_name=first_name,
last_name=last_name
)
kanga_user.preferred_language = translation.get_language()
kanga_user.registered = registered
kanga_user.save()
return kanga_user
Here is the test
@mock.patch('model.models.KangaUser')
@mock.patch('django.contrib.auth.models.User')
def test_create(self, UserMock, KangaUserMock):
# Mocking configuration
# User
UserMock.objects = mock.MagicMock()
UserMock.objects.create_user = mock.MagicMock()
user_return_value = User(id=1)
UserMock.objects.create_user.return_value = user_return_value
# 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
UserMock.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(models.KangaUser.save.called)
The first test (UserMock.objects.create_user.assert_called_with) is ok, but for the second, I always end up having "AssertionError: False is not true"
Upvotes: 8
Views: 8515
Reputation: 1337
@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)
Upvotes: 0
Reputation: 61
With pytest-mock you can do:
def test_create(self, mocker):
mocker.patch("path_to_the_kanga_user_manager.KangaUser.save")
Upvotes: 0
Reputation: 377
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 !
Upvotes: 13