[Django]-How to correct test for unicode strings with Django and Python 2

4πŸ‘

βœ…

tl; dr ) Use the same type on both sides of assertEqual.

The best code, universal for Python 3 and 2 is possible without adding symbols like u'', unicode, foo.__str__() etc. Less code should be written and more thought about both what type is expected where in Python 2/3.

part A) fix the test

A short (ugly) solution is to use text_type on the left side if you use the same explicit type u'...' on the right side. Avoid to use functions with underscores due to better readability. You can convert a value to text_type by several ways:

Replace the line in test by

self.assertEqual(u'%s' % self.cat1, cat_result1)

or

self.assertEqual(u'{}'.format(self.cat1), cat_result1)

or

from django.utils.six import text_type
self.assertEqual(text_type(self.cat1), cat_result1)

A nicer solution is to unify the type in your module and to use from __future__ import unicode_literals at the beginning of your module because you work mostly with texts not with binary data. Than you can remove all u', but it is still useful until everything works in both versions.

part B) fix the __str__ method

Your code would fail if both the parent category name and the current name are not in ASCII. Fix:

from __future__ import unicode_literals
# ... many lines

def __str__(self):
    if self.parent is None:
        return ('{}'.format(self.name))
    else:
        return ('%s%s%s' % (
            self.parent,
            settings.PARENT_DELIMITER,
            self.name)
        )

I only removed the __str__() call and added the future directive because the models.py is the first module where it is especially useful. Otherwise you should add u' to both format strings here.

It is useful to know what the python_2_unicode_compatible decorator does. The result of __str__ method should be text_type (unicode in Python 2), but if you call it directly in Python 2 you get bytes type. The formatting operator selects the matching method but any explicit method is invalid either in Python 3 or in 2. Non ascii values of different types can not be combined.

πŸ‘€hynekcer

1πŸ‘

have you tried using six.text_type?

six.text_type

Type for representing (Unicode) textual data. This is unicode() in Python 2 and str in Python 3.

edit: you don’t have to install six, as the needed methods are present in django.utils.six – thanks @hynekcer for pointing it out

Six provides simple utilities for wrapping over differences between Python 2 and Python 3. It is intended to support codebases that work on both Python 2 and 3 without modification. six consists of only one Python file, so it is painless to copy into a project.~

πŸ‘€zsepi

0πŸ‘

According to error message test tries to compare str and unicode objects. That’s not good generally.

AssertionError: 'Category 1->Category \xc3\xbc' != u'Category 1->Category \xfc'

Try to:

  • compare unicode objects: self.assertEqual(self.cat1, cat_result1)
  • always work with unicode locale even if at a moment you are using only latin1-symbols

Leave a comment