Data Faking in Django
In my first ever project as a software engineer, I was assigned to write a significant chunk of tests for a standard django web app. Having never written django tests before, naturally I came across a few speed bumps along the road to full coverage. The biggest issue I had to overcome was data-faking. Some parts of an app, for example, any code that follows a form submission, can’t be accurately tested unless expected data is valid. So since almost all websites involve a myriad of different kinds of data, it’s essential to add data-faking to your testing toolbox. I will first go over how to fake files such as pdf and png, and then move to user permissions.
The first type of data-faking I will go over is the faking of files; both image files (e.g, like pngs) and text files (e.g pdfs). This is a necessary step to add coverage to forms.py or models.py which use ImageFields or FileFields. It also can be necessary in testing any views.py that submit a form or create a model object.
Faking text files is less complex than images and can be done a couple of different ways. The simplest method is to use the Python class SimpleUploadedFile
in combination with ContentFile
to create file data. To fake images in model tests you can also use SimpleUploadedFile
, just remember to set the content_type = “image/png”
as I did in the snippet below. Faking images for forms is a little more complicated due to the way forms validate data. In order to properly fake an image for a form, you must use InMemoryUploadedFile
in order to fake both the file itself and a memory location to trick the validator. For more details, you can refer to the code snippet below, which shows you how to create fake file data from a b64 encoded image, how to use both SimpleUploadedFile
and InMemoryUploadedFile
to convert that file data into a fake file, and how to assign the proper values to every parameter along the way. I usually add this code snippet to the setUp()
method of my test classes so I have fake files and images at the ready.
image_data = b64decode("R0lGODlhAQABAIABAP8AAP///yH5BAEAAAEALAAAAAABAAEAAAICRAEAOw==") image_file = ContentFile(image_data, 'one.GIF') image = InMemoryUploadedFile( StringIO(image_data), field_name='tempfile', name='one.GIF', content_type='image/gif', size=len(image_data), charset='utf-8', ) self.in_memory_image = image self.uploaded_image_file = SimpleUploadedFile( image_file.name, image_file.read(), content_type="image/png" ) self.uploaded_pdf = SimpleUploadedFile( image_file.name, image_file.read(), content_type="image/pdf" )
The second type of data-faking is the faking of users and permissions. To test any page that requires a user to log in or only is visible to certain users, you need to fake that log in with the correct permissions in your tests. Treat tests like real users, if a test doesn’t log in, it can’t add coverage to a page that requires a login. Faking user logins is fairly simple, just create the type of user you need in the setUp()
method using get_user_model()
which returns the currently active user model class, and provide a client and a session for your tests. Client
is a python class that serves as a fake Web browser, which allows your tests to interact with the site as a user would. Also don’t forget to login in your test like I do on the first line of test_user_details_form()
!
def setUp(self): # Provide a client and session for the tests self.client = Client() self.session = self.client.session self.user = get_user_model().objects.create_user(username='tester', email='test@test.com', password='password') def test_user_details_form(self): login = self.client.login(username="tester", password="password") self.assertEqual(first_name, self.user.first_name)
If you’re having trouble testing anything that involves data-faking and are frustrated with the generic “invalid form” error, you can get the errors you want with a simple assert statement. To see the full form errors all you need is to assert that form.errors
is equal to any random string, thereby ensuring the assertEqual
will fail, and display the value of form.errors
in the failure message. This technique of using assert statements to debug failing tests is very useful and with just a little creativity, you can figure out why even the trickiest tests are failing. Data-faking can be incredibly frustrating, but once you get it working, you can use your solution over and over.