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:
- 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).
- Execute the
update_dog_from_data
function, passing in the dog data and the test dog object. - 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