2

I have a web app that I am unit testing. I'm trying to test the drf serializers, but the serializers change the format of datetimes, so identical dates formatted differently are failing the test because they're not identical.

I've tried formatting the serializer timeDateField, but all of the strftime formats they use are zero-padded, and my test Users last-login attribute gives the date without zero-padding. I think the solution could be solved the way that the selected answer describes here but that seems a bit sketchy, and ideally, I, as someone who will have to maintain this code, want a cleaner or more pythonic solution.

Here is the test:

class UserTest(TestCase):

    def setup(self):

        last_login = datetime.datetime(2000, 1, 1, hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
        date_joined = datetime.datetime(2019, 2, 2, hour=2, minute=2, second=2, microsecond=2, tzinfo=None)
        birthdate = timezone.now().date()
        renewal = timezone.now().date()

        return User.objects.create(username='test_user', first_name='test_first', last_name='test_last',
                                   email='test_mail', last_login=last_login, date_joined=date_joined,
                                   birthdate=birthdate, gender='U', renewal=renewal)

    def test_user_serializer(self):
        self.u = self.setup()
        serializer = UserSerializer(self.u, many=False)
        data = serializer.data
              .....
    # tests for serialized attributes
        self.assertEquals((data['last_login']), self.u.last_login)  # FAILING THE TEST

Here is the UserSerializer:

class UserSerializer(serializers.ModelSerializer):

    last_login = serializers.DateTimeField
    date_joined = serializers.DateTimeField
    birthdate = serializers.DateField
    renewal = serializers.DateField

    class Meta:
        model = User
        fields = ('__all__')

Here is the error I am getting:

self.assertEquals(data['last_login'], self.u.last_login)
AssertionError: '2000-01-01T00:00:00Z' != datetime.datetime(2000, 1, 1, 0, 0)

In the model, the last_login attribute is a dateTimeField.

Should I be trying to change the formats of one of the elements being tested or is there a way to write a test that compares the dates irrespective of format?

Please advise. Thank you.

Brian
  • 385
  • 1
  • 5
  • 23
  • 1
    your issue isn't exactly due to datetimes being "formatted" differently, but rather due the fact that you're comparing two different objects, a serialized datetime (string) and an actual datetime object. to overcome that without anything "hacky", you can either format the datetime as a string, or parse the datetime string into an actual datetime object with something like [this](https://dateutil.readthedocs.io/en/stable/parser.html). – henriquesalvaro Jun 27 '19 at 01:19

3 Answers3

5

The simplest way to do that is:

assert response.json()["created_at"] == serializers.DateTimeField().to_representation(instance.created_at)
stefanitsky
  • 443
  • 7
  • 9
4

The reason for your error is due to the fact that you are trying to do a comparison of two different datatypes/objects. In the below line:-

'2000-01-01T00:00:00Z' != datetime.datetime(2000, 1, 1, 0, 0)

On the LHS we have a string (of ISO 8601 Timestamp Format) and in the RHS we have a datetime object. Therefore, the above operation will always produce an error as the type of data isn't the same.

So you have two choices, either you can convert the RHS (datetime object) into a string, using a function like strftime(). Or you can create a datetime object out of the LHS (timestamp string).

I will explain the first method, as it is the easier one of the two.

So in order to convert a datetime object, into a string, we have to pass it via strftime().

strftime() takes as argument a datetime object**/tuple, and converts it into a string according to format specification.

so if we pass datetime.datetime(2000, 1, 1, 0, 0) via strftime() with the ISO 8601 format specification, then we get the desired format.

Example:-

import datetime

datet_obj = datetime.datetime(2000, 1, 1, 0, 0)

datet_str = datet_obj.strftime("%Y-%m-%dT%H:%M:%SZ")

print(datet_str)

Output:-

'2000-01-01T00:00:00Z'

In this way you can convert the datetime objects into strings of desired format.

Now if we create a one liner for it all, and then do the comparison i.e.

'2000-01-01T00:00:00Z' != datetime.datetime(2000, 1, 1, 0, 0).strftime("%Y-%m-%dT%H:%M:%SZ")

The above code run's successfully and outputs False, as both the LHS and RHS are equal.

** A datetime Object is generally the output of strptime().

Vasu Deo.S
  • 1,820
  • 1
  • 11
  • 23
0

Some modifications to Vasu Deo.S's answer which include microsecond in the formatting.

import datetime

datet_obj = datetime.datetime(2000, 1, 1, 0, 0)

datet_str = datet_obj.strftime("%Y-%m-%dT%H:%M:%S.%fZ")

print(datet_str)

Output

2000-01-01T00:00:00.000000Z
Sin Hau Goh
  • 35
  • 2
  • 4