Reputation: 357
I have a test class in Django Rest Framework which contains a function which passes the test if it is the only function in the class, or fails if it is one of two or more functions.
If I run the below code as is, i.e. def test_audio_list(self)
function remains commented out, the test passes as per the two assert statements.
If I uncomment def test_audio_list(self)
function and run the tests, the def test_audio_retrieve(self)
function will fail with a 404 whilst def test_audio_list(self)
will pass.
Here is my full test case (with def test_audio_list(self):
commented out).
class AudioTests(APITestCase):
# setup a test user and a factory
# factory = APIRequestFactory()
test_user = None
def setUp(self):
importer()
self.test_user = User(username='jim', password='monkey123', email='[email protected]')
self.test_user.save()
# def test_audio_list(self):
# """
# Check that audo returns a 200 OK
# """
# factory = APIRequestFactory()
# view = AudioViewSet.as_view(actions={'get': 'list'})
#
# # Make an authenticated request to the view...
# request = factory.get('/api/v1/audio/')
# force_authenticate(request, user=self.test_user)
# response = view(request, pk="1")
#
# self.assertContains(response, 'audio/c1ha')
# self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_audio_retrieve(self):
"""
Check that audo returns a 200 OK
"""
factory = APIRequestFactory()
view = AudioViewSet.as_view(actions={'get': 'retrieve'})
# Make an authenticated request to the view...
request = factory.get('/api/v1/audio/')
force_authenticate(request, user=self.test_user)
response = view(request, pk="1")
self.assertContains(response, 'audio/c1ha')
self.assertEqual(response.status_code, status.HTTP_200_OK)
Running python manage.py test
on the above will produce no errors. But if I uncomment def test_audio_list(self)
function, def test_audio_retrieve(self)
will fail with the following:
======================================================================
FAIL: test_audio_retrieve (api.tests.AudioTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/code/api/tests.py", line 96, in test_audio_retrieve
self.assertContains(response, 'audio/c1ha')
File "/usr/local/lib/python3.7/site-packages/django/test/testcases.py", line 446, in assertContains
response, text, status_code, msg_prefix, html)
File "/usr/local/lib/python3.7/site-packages/django/test/testcases.py", line 418, in _assert_contains
" (expected %d)" % (response.status_code, status_code)
AssertionError: 404 != 200 : Couldn't retrieve content: Response code was 404 (expected 200)
Note. If I change the order of the functions in the class, i.e. place def test_audio_retrieve(self)
before def test_audio_list(self)
, the result is still the same.
I'm assuming I must be doing something wrong here, but I just cannot put my finger on it.
Any help is much appreciated.
Cheers,
C
Upvotes: 2
Views: 1272
Reputation: 357
The issue here as I have since had explained to me by a very helpful user on Django's Forums is quite straightforward.
https://forum.djangoproject.com/t/apitestcase-with-apirequestfactory/420
I am using an auto-increment field on the ID of my objects, and as Django performs each test, it first imports the data with the call to importer()
in def setUp(). After the each test function is run, Django then deletes the data and will again run
setUp()which in turn run
importer()```.
This causes the ID of my objects to change with each test function within the class that is run.
I was statically defining the IDs in my test case thinking that the objects ID would remain the same throughout the entire test case.
So instead of
# hardcoded PK
response = self.detail_view(request, pk="1")
I now have
# dynamically assigned PK
pk = Audio.objects.first().id
response = self.detail_view(request, pk=pk)
So long story short, I will need to dynamically obtain the ID of an object I want to test, or better yet, create a test case which doesn't rely on an import of data. It was recommended to me to use something like Factory Boy to generate my own test data within the test cases.
Upvotes: 2
Reputation: 41
you used 'force_authenticate' more than one time, maybe you can authenticate user in setUp or use refresh_from_db
please check this: Note: force_authenticate directly sets request.user to the in-memory user instance. If you are re-using the same user instance across multiple tests that update the saved user state, you may need to call refresh_from_db() between tests.
https://www.django-rest-framework.org/api-guide/testing/
...
can you try merge functions, something like this,
def test_audio(self):
"""
Check that audio returns a 200 OK
"""
factory = APIRequestFactory()
view_retrieve = AudioViewSet.as_view(actions={'get': 'retrieve'})
view_list = AudioViewSet.as_view(actions={'get': ‘list’})
# Make an authenticated request to the view...
request = factory.get('/api/v1/audio/')
force_authenticate(request, user=self.test_user)
# Retrieve
response = view_retrieve(request, pk="1")
self.assertContains(response, 'audio/c1ha')
self.assertEqual(response.status_code, status.HTTP_200_OK)
# List
response = view_list(request, pk="1")
self.assertContains(response, 'audio/c1ha')
self.assertEqual(response.status_code, status.HTTP_200_OK)
Upvotes: 2