Test driven development with Pytest and Django
In this part, we will create our Task app and continue with Test-driven development we started previously on Test driven development with Pytest, Django and Docker — Set up.
The app
Let’s create the app
python manage.py startapp tasks
To include the app in our project, we need to add a reference to its configuration class in the INSTALLED_APPS
setting:
INSTALLED_APPS = [
...
...
'tasks.apps.TasksConfig',
]
Delete the test.py file from the task app. Instead, create a new directory called tests/ inside the task app, and create an __init__.py file for it to be a python package.
We will be creating new test files (inside the task’s tests/ directory) for each file that we are going to test with Pytest. For example, for models.py we will create test_models.py.
And since we are following Test Driven Development, we will create our tests first.
The models
Create a tasks/tests/test_models.py and add the following
import pytest
from mixer.backend.django import mixer@pytest.mark.django_db
class TestTask:
def test_model(self):
obj = mixer.blend('tasks.Task')
assert obj.pk == 1, 'Should create a task instance'
Run pytest
and you can expect similar resuls:
We got our first Red!
Remember TDD is all about Red — Green — Refactor cycle:
- Red: Write test cases first and check the Errors
- Green: Solve the error
- Refactor: If need be then refactor the code
Now that we got our error, it is telling us there is no Task class in our tasks app. Let’s create our Task model.
Update tasks/models.py with the following:
from django.db import modelsclass Task(models.Model):
title = models.CharField(max_length=100)
is_completed = models.BooleanField(default=False)
Now run the test again and you can expect similar results:
Our task model will also have an excerpt method for its title, and so it will return the first few characters from the title. So let’s first update our test for that.
Update tasks/tests/test_models.py
import pytest
from mixer.backend.django import mixer@pytest.mark.django_db
class TestTask:
def test_model(self):
obj = mixer.blend('tasks.Task')
assert obj.pk == 1, 'Should create a task instance' def test_excerpt(self):
obj = mixer.blend('tasks.Task', title='Buy milk from the market')
result = obj.get_excerpt(8)
assert result == 'Buy milk', 'Should return first few characters'
Run the test and you can expect similar results:
Since we don’t have a get_excerpt
method, the error is telling us about that.
Let’s update our tasks/models.py to have our green!
from django.db import modelsclass Task(models.Model):
title = models.CharField(max_length=100)
is_completed = models.BooleanField(default=False) def get_excerpt(self, total_char):
return self.title[:total_char]
Run the test again to check if the test has passed.
The admin
As we did with testing our models, we will create our tests first for the admin.py as well.
Create a tasks/tests/test_admin.py file and add the following:
from .. import models
from .. import admin
from django.contrib.admin.sites import AdminSite
import pytest
from mixer.backend.django import mixer@pytest.mark.django_db
class TestTaskAdmin:
def test_excerpt(self):
site = AdminSite()
task_admin = admin.TaskAdmin(models.Task, site) obj = mixer.blend('tasks.Task', title='Buy milk from the market')
result = task_admin.excerpt(obj)
assert result == 'Buy milk', 'Should return first few characters'
Run the test and you can expect similar output:
Since there is no TaskAdmin in our admin.py, we will need to write that out. Update task/admin.py with the following:
from django.contrib import admin
from . import modelsclass TaskAdmin(admin.ModelAdmin):
model = models.Task
list_display = ('excerpt',) def excerpt(self, obj):
return obj.get_excerpt(8)admin.site.register(models.Task, TaskAdmin)
Run the test again, and you can expect similar output:
Yay! Now that, most of the app is tested. We will see how to follow test driven development with the views.py and Django rest framework in the next part.
If you want, you can check out the repo.
See you soon!