How to write unit test ValidationError case in response by use client.post()?

Question:

I have a model with a time validator raise ValidationError('End time cannot be earlier than start time')

So I want to write a unit test using client.post() with data invalid (from_time > to_time), and I expected ValidationError to appear in this test.

    raise ValidationError('End time cannot be earlier than start time')
django.core.exceptions.ValidationError: ['End time cannot be earlier than start time']
Asked By: Kev

||

Answers:

you can take a look at the document example on how to write test case https://docs.djangoproject.com/en/dev/topics/testing/tools/#example. In your case it would be like so(notice that this is just an example, so modify to fit your case):

This is for validating from serializer/api of DRF:

import unittest
from django.test import Client
import datetime

class SimpleTest(unittest.TestCase):
    def setUp(self):
        # Every test needs a client.
        self.client = Client()

    def test_invalid_date(self):
        # Issue a POST request.
        response = self.client.post(
                   '/your/path/url',
                   {
                      'start_time': datetime.datetime(2020, 5, 17), 
                      'end_time': datetime.datetime(2020, 5, 15) #notice end_time smaller than start_time
                   },
              )

        self.assertEqual(response.status_code, 400)

        # Check that the rendered context json have error message.
        self.assertEqual(response.json()['key']['path']['to']['error']['message'], 'End time cannot be earlier than start time')

This is for validating from model validator(doc):

for example you have your model validator like so in your model:

def custom_validator(value):
    if value.end_time < value.start_time:
      raise ValidationError('End time cannot be earlier than start time')

Your unit test will be like this, use python assertRaisesRegex() to check for python ValidationError type:

import unittest
from django.test import Client
import datetime

class SimpleTest(unittest.TestCase):

    def test_invalid_date(self):
        with self.assertRaisesRegex(ValidationError, 'End time cannot be earlier than start time'):
             your_model = YourModel(
               start_time=datetime.datetime(2020, 5, 17), 
               end_time=datetime.datetime(2020, 5, 15) 
             )
             your_model.full_clean()
Answered By: Linh Nguyen

When using pytest-django your test would look as simple as this:

from datetime import datetime, timedelta

def test_error_when_to_time_before_from_time(db, admin_client):
    invalid_data = {
        "from_time": datetime.today(),
        "to_time": datetime.today() - timedelta(days=2),
    }
    response = admin_client.post("<url-to-endpoint>", data=invalid_data)
    assert response.status_code == 400
    assert "End time cannot be earlier than start time" in response.content.decode()

Pytest-django gives you a logged in admin client and creates a temporary database depending on your current migrations for every test. After the test the changes are discarded again.

I also added "TEST_REQUEST_DEFAULT_FORMAT": "json", to the REST_FRAMEWORK dictionary in settings.py.

Answered By: Hans Bambel