Search code examples
djangodjango-rest-frameworkdjango-testing

Django Rest Framework testing save POST request data


I'm writing some tests for my Django Rest Framework and trying to keep them as simple as possible. Before, I was creating objects using factory boy in order to have saved objects available for GET requests.

Why are my POST requests in the tests not creating an actual object in my test database? Everything works fine using the actual API, but I can't get the POST in the tests to save the object to make it available for GET requests. Is there something I'm missing?

from rest_framework import status
from rest_framework.test import APITestCase

# from .factories import InterestFactory


class APITestMixin(object):
    """
    mixin to perform the default API Test functionality
    """
    api_root = '/v1/'
    model_url = ''
    data = {}

    def get_endpoint(self):
        """
        return the API endpoint
        """
        url = self.api_root + self.model_url
        return url

    def test_create_object(self):
        """
        create a new object
        """
        response = self.client.post(self.get_endpoint(), self.data)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data, self.data)
        # this passes the test and the response says the object was created

    def test_get_objects(self):
        """
        get a list of objects
        """
        response = self.client.get(self.get_endpoint())
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data, self.data)
        # this test fails and says the response is empty [] with no objects


class InterestTests(APITestCase, APITestMixin):
    def setUp(self):
        self.model_url = 'interests/'
        self.data = {
            'id': 1,
            'name': 'hiking',
        }

        # self.interest = InterestFactory.create(name='travel')
        """
        if I create the object with factory boy, the object is 
        there. But I don't want to have to do this - I want to use
        the data that was created in the POST request
        """

You can see the couple lines of commented out code which are the object that I need to create through factory boy because the object does not get created and saved (although the create test does pass and say the object is created).

I didn't post any of the model, serializer or viewsets code because the actual API works, this is a question specific to the test.


Solution

  • First of all, Django TestCase (APITestCase's base class) encloses the test code in a database transaction that is rolled back at the end of the test (refer). That's why test_get_objects cannot see objects which created in test_create_object

    Then, from (Django Testing Docs)

    Having tests altering each others data, or having tests that depend on another test altering data are inherently fragile.

    The first reason came into my mind is that you cannot rely on the execution order of tests. For now, the order within a TestCase seems to be alphabetical. test_create_object just happened to be executed before test_get_objects. If you change the method name to test_z_create_object, test_get_objects will go first. So better to make each test independent

    Solution for your case, if you anyway don't want database reset after each test, use APISimpleTestCase

    More recommended, group tests. E.g., rename test_create_object, test_get_objects to subtest_create_object, subtest_get_objects. Then create another test method to invoke the two tests as needed