Hi everyone, i’ve started to learn testing in django recently and i’m trying to write a testcase for my notifications view and having some issues probably due to how test environment handles database.
I have UserNotification, FriendshipRequest models and normally when a FriendshipRequest object gets created, a UserNotification object gets created through signals.
And I need to retrieve that notification object in order to test my views since i need its id for the url parameter. Is there a way to retrieve them inside the test environment because when i try to get the notification objects that created through signals inside my test functions it returns “None” or i have to “simulate” the workflow of the signal and create the notification object manually inside setUp()?.
(Signals registered correctly and works as expected). Here’s the test code:
class TestAcceptDeclineNotifications(APITestCase):
def setUp(self):
self.user = Profile.objects.create(firstName='test', lastName='test',email='testexample@email.com', username='testuser', password='password123')
self.test_user = Profile.objects.create(firstName='test', lastName='test',email='testexample2@email.com', username='testuser2', password='password123')
self.test_user_id = self.test_user.id
self.friendship = FriendshipRequest.objects.create(initiator=self.test_user,recipient=self.user, status='pending')
self.friend_content_type = ContentType.objects.get_for_model(self.friendship)
self.friend_notification = UserNotification.objects.filter(content_type=self.friend_content_type, object_id=self.friendship.id).first()
self.friend_notification_id = self.friend_notification.id
self.token, _ = Token.objects.get_or_create(user=self.user)
self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
def test_decline_friendship_view(self):
url = reverse('decline_friendship', args=[self.friend_notification_id])
response = self.client.put(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_accept_friendship_view(self):
url = reverse('accept_friendship', args=[self.friend_notification_id])
response = self.client.put(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
I don’t fully understand what you’re actually trying to test, or where in the tests you’ve shown you need to get this notification object.
But each test should start with an empty database, so you could just retrieve the notification from the database:
notification = UserNotification.objects.first()
So long as your test won’t have created any others.
If your test is already creating a UserNotification
object, like in the setUp()
method you shared, you’ll need to exclude those. So in that case maybe:
notification = UserNotification.objects.exclude(pk=self.friend_notification_id).first()
1 Like
Hi, First of all thank you for your response and sorry if i underexplained it. In the code i shared i’m trying to test the api views “accept_friendship” and “decline_friendship” and here
url = reverse('decline_friendship', args=[self.friend_notification_id])
in the url i must specify the “id” of the notification object that created inside “signals.py” and the signal is being triggered when a “FriendshipRequest” object is created.
Here i create the FriendshipRequest object:
self.friendship = FriendshipRequest.objects.create(initiator=self.test_user,recipient=self.user, status='pending')
So when the “FriendshipRequst” object gets created (like i do above), a “UserNotification” object must be created via signals.
So in the test case, i assume that the “UserNotification” object is already created and i’m trying get the “UserNotification” object that has the “FriendshipRequest” object that i just created as Content_Object: (My UserNotification model has a “GenericForeignKey” field)
self.friend_content_type = ContentType.objects.get_for_model(self.friendship)
self.friend_notification = UserNotification.objects.filter(content_type=self.friend_content_type, object_id=self.friendship.id).first()
self.friend_notification_id = self.friend_notification.id
But i can’t get the UserNotification object that created inside signals.py, it returns None.
What i’m asking is: Django tests run in a transactional, isolated test databases and maybe thats why i cant retrieve the notification object that created via signals, is there a way to get those objects in a test environment or do i have to create all the necessary objects manually inside setUp() method? And how to even test signals that interacts with the database. Thank you for your response again <3.
Thanks for the explanation. I would have expected this to work. What happens, in the test if you do
print(UserNotification.objects.all())
Are there any in the database at that point, as you’d expect?
It only gets created once:
NOTs1
<QuerySet [<UserNotification: friend_request notification for testuser>]>
NOTs2
<QuerySet []>
There are 2 tests inside the testcase and on the first time it seems like the notification object gets created on the first time setUp runs but on the second time it doesn’t.
For more context, it’s a React & Django messaging platform project and i tested the behaviour live on the browser and everything works as expected the notification object gets created when friendship request object gets created and sends it to frontend so everything works as expected when testing on the browser xd but inside the testcase the notification object is not getting created for the 2. time
I don’t know I’m afraid, I don’t know what else to suggest from here.
UPDATE
I still couldn’t find a way to retrieve the notification object created by the signal but i can test if the signal actually works as expected with “unittest.patch” see the doc
@patch('notifications.signals.create_notification')
def test_accept_friendship_view(self,mock_not):
self.friendship = FriendshipRequest.objects.create(initiator=self.test_user,recipient=self.user, status='pending')
self.friend_notification_id = self.friend_notification.id
mock_not.assert_called_with(self.friendship.recipient, 'friend_request',self.friendship, False)
In the code i shared, I create a “FriendRequest” object:
self.friendship = FriendshipRequest.objects.create(initiator=self.test_user,recipient=self.user, status='pending')
And then, thanks to the “patch” decorator i can assert if the related signal function (create_notification) worked as expected or not, and created a notfication object or didn’t.
mock_not.assert_called_with(self.friendship.recipient, 'friend_request',self.friendship, False)
“assert_called_with()” asserts if the related function has run with the parameters provided.
So i can test the signal if it runs as expected it creates a notification object just like i tested on the browser.
The problem actually solved since i manually create the notification object inside setUp() and test the signal behaviour seperately. But is there a way to retrieve the objects created inside signals in the test environment?
I haven’t had any problems like this when testing signals myself - objects are created as expected by the signals. I suspect anyone is going to need to see the code being tested in order to see what might be going wrong.