Django Testing: Dynamically Accessing Attributes in Unit Tests

django python testing til

In this blog post, we’ll explore a way to write unit tests for a Django model using Model Mommy. Our main focus is on the update_dog_from_data function, which updates a Dog object with JSON data. However, the highlight is the technique to dynamically access attribute values in the tested object. By the end, you’ll learn some unit testing strategies and gain insights into dynamically interacting with object attributes during testing.

Django Model Setup

Let’s start by considering a Django model called Dog. It has several fields, but for this example, only the “name” field is required. Here’s the model definition:

from django.db import models

class Dog(models.Model):
    name = models.CharField(max_length=100)
    breed = models.CharField(max_length=100, blank=True)
    age = models.PositiveIntegerField(blank=True, null=True)
    color = models.CharField(max_length=100, blank=True)
    weight = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)

    def __str__(self):
        return self.name

To create a Dog instance, you only need to provide the “name” field:

max_the_dog = Dog(name="Max")
max_the_dog.save()

Testing Function Overview

Now, let’s focus on the update_dog_from_data function, which updates a Dog object with JSON data. For the sake of brevity, I won’t include the actual implementation here. Instead, let’s assume there’s some great and innovative code within the function. 🤭

Creating Mock Objects

To test this function, we’ll use the Model Mommy library to create mock objects. Please note that Model Mommy is no longer maintained and has been replaced with Model Bakery, which works similarly. Here’s an example of creating a mock Dog object:

dog_name = 'Doggo'
test_dog = mommy.make(Dog, name=dog_name)

Writing the Unit Test

Now, let’s dive into writing the unit test itself.

In our test, we want to ensure that the update_dog_from_data function correctly updates the Dog object with the provided data. To achieve this, we’ll follow these steps:

  1. Check the initial values of the mock dog object and ensure they differ from the final expected values (which are obtained from the external data).
  2. Execute the update_dog_from_data function, passing in the dog data and the test dog object.
  3. Verify that the values have been updated correctly.

Initially, I attempted to compare each value individually, but I wanted a more efficient and lazy approach. I found out that I could use eval and f-strings, which worked perfectly in this case (note: this approach is safe here because we have control over the data, but it’s not recommended for user input).

Let’s take a look at the final test implementation:

class TestUpdateDog(TestCase):

    def test_success_dog_upadted(self):
        dog_name = 'Doggo'
        test_dog = mommy.make(Dog, name=dog_name)
        
        dog_data = {
            'name': 'Max',
            'breed': 'Labrador Retriever',
            'age': 5,
            'color': 'black',
            'weight': 29.5
        }

        for key, value in dog_data.items():
            dog_value = eval(f"test_dog.{key}")
            assert value != dog_value

        update_dog_from_data(dog_data, test_dog)

        for key, value in dog_data.items():
            new_dog_value = eval(f"test_dog.{key}")
            assert value == new_dog_value

In the above code, we iterate through the dog_data dictionary, comparing each value with the corresponding attribute value of the test dog object. We use eval and f-strings to dynamically access the attribute values. After calling the update_dog_from_data function, we repeat the comparison to verify that the values have been updated correctly.

With this approach, we can efficiently test the update_dog_from_data function and ensure its correctness.

Happy testing!


If you found this helpful, please share this article!

The post Django Testing: Dynamically Accessing Attributes in Unit Tests was originally published at flaviabastos.ca

Related Posts