Search code examples
pythondjangounit-testingdjango-rest-framework

Django rest framework: Unit testing post request gets status code 400


I'm doing some unit tests in this restaurant app, and the API request to cancel orders returns code 400 when running "manage.py test" command, but doing the same request on Postman works fine (with the server also running on my local machine with the "manage.py runserver" command). I think it's something I'm doing wrong with the unit test, but I'm not sure. In settings, Debug = True and ALLOWED_HOSTS = ['*'] (but I also tried ALLOWED_HOSTS = []). Here's the code:

tests.py

class CancelOrderAPITest(APITestCase):

    def setUp(self):
        test_product = Products.objects.create(id=1, name='testProduct', description='-test-', price=2.56)
        test_product.save()
        test_order = Order.objects.create(table=1, status='WA')
        test_order.save()
        test_order.product.add(test_product)

        self.user = User.objects.create(username='test')
        self.user.set_password('passtest')
        self.user.save()

        Payments.objects.create(value=0.0, user=self.user)
        Token.objects.create(user=self.user)

    def test_CancelWithCredentials(self):
        check_login = self.client.login(username='test', password='passtest')
        self.assertTrue(check_login)

        token = Token.objects.get_or_create(user=self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f'Token {token[0].key}')

        data = {"table": 1}

        response = self.client.post(reverse('cancel-order'), data=data, content_type='application/json')
        order = Order.objects.filter(table=data['table']).order_by('date')[0]

        self.assertEqual(response.status_code, status.HTTP_200_OK)  # returning 400. Investigate further
        self.assertEqual(order.status, Order.Status.CANCELED)

views.py

class CancelOrder(APIView):
    # REST API view for waiters to cancel orders. The waiter must be an authenticated user
    permission_classes = (IsAuthenticated,)
    parser_classes = (JSONParser,)

    def post(self, request):
        data = request.data

        try:
            order = Order.objects.filter(table=data['table']).order_by('date')[0]
            order.status = Order.Status.CANCELED
            order.save()
            resp = {"status": "Order canceled!"}
        except ObjectDoesNotExist:
            resp = {"exception": "Couldn't find requested product!"}

        return Response(resp)

models.py

class Order(models.Model):

    class Status(models.TextChoices):
        WAITING = 'WA', _('Waiting')
        DELIVERED = 'DE', _('Delivered')
        PARTIAL_DELIVER = 'PD', _('Partially Delivered')
        PREPARING = 'PP', _('Preparing')
        CANCELED = 'CA', _('Canceled')
        PAID = 'PA', _('Paid')

    product = models.ManyToManyField(Products)
    table = models.IntegerField(default=1)
    status = models.CharField(max_length=100, choices=Status.choices, default=Status.WAITING)
    date = models.DateTimeField(default=datetime.datetime.now)
    payment = models.ForeignKey(Payments, on_delete=models.DO_NOTHING, default=1)

Solution

  • I figured it out, thanks to the @anonymous comment.

    For some reason the unit test was sending the data in the request with single quotes causing a parse error, even though I was declaring it with double quotes.

    Solved by removing the parameter content_type="application/json and adding the parameter format="json" to the request.