Django's Unit Tests and Integration Tests
Testing was indeed often overlooked in the past, especially for rapid development frameworks like Python Web.
目录8 个章节
Preface
It's been a while since I worked with Django, and recently maintaining a project I built with it gave me a strangely nostalgic feeling 😂
Testing, in the past, was indeed a topic often overlooked—especially for rapid-development frameworks like Python Web, where agility was prioritized. The approach was "build fast, launch fast" rather than "take time to do it right." This rough development style often made the project hard to maintain later on, which is how Python got the reputation of being easy to develop with but hard to maintain.
Python: I'm not taking the blame for that 😒
Getting back on topic, the testing in this case involves two parts: writing unit tests and integration tests within the Django project to ensure its functionality works correctly. Then, I also developed an independent automated testing tool that can test based on OpenAPI documentation and output a test report afterward. The report includes details like whether each API endpoint passed the test and the response time, etc.
I built this tool using Go, mainly because Go makes cross-compilation straightforward. The generated executable can be directly uploaded to a server and executed, which is incredibly convenient.
I’ll introduce this automated testing tool in the next post.
Testing in Django
I must say, Django's documentation is really excellent 👍
After reading the documentation for a while, I started writing tests. The full Django suite is truly comfortable, and the development experience is incredibly smooth.
When using startapp to create an app, each app directory contains a tests.py file. We can write our test code in this file.
If there are many test cases, you can split them. Refer to how views.py is split—convert tests.py into a package. Create a tests directory, place individual test files inside it, and import them in __init__.py.
Testing is divided into unit testing and integration testing. Writing unit tests in Django is much more comfortable than in AspNetCore. You don't need to worry about dependency injection—Django handles it all for you.
During testing, Django automatically creates a test database, so you don't have to deal with environment isolation yourself.
Regarding the differences between these two types of testing, I covered this in a previous article, so I won't copy and paste it here.
Asp-Net-Core Learning Notes: Unit Testing and Integration Testing
Examples
This time, we'll use two apps as examples
- config - unit tests (单元测试)
- dashboard - integration tests (集成测试)
In Django, both types of tests come with minimal mental overhead and require no additional setup. You can simply write them directly in tests.py.
Unit Testing
Using the config app as an example for unit testing.
I have encapsulated a ConfigService with the following code:
from datetime import date
from .models import CommonConfig
class ConfigService(object):
def __init__(self):
...
@staticmethod
def get_config(key: str) -> str:
queryset = CommonConfig.objects.filter(key=key)
if queryset.exists():
return queryset.first().value
return ''
@property
def today(self):
return date.today()
@property
def start_year(self):
value = self.get_config('start_year')
return str(self.today.year) if len(value) == 0 else value
@property
def start_month(self):
value = self.get_config('start_month')
return str(self.today.month) if len(value) == 0 else value
@property
def end_year(self):
value = self.get_config('end_year')
return str(self.today.year) if len(value) == 0 else value
@property
def end_month(self):
value = self.get_config('end_month')
return str(self.today.month) if len(value) == 0 else value
Edit apps/config/tests.py
from django.test import TestCase
from apps.config.models import CommonConfig
from apps.config.services import ConfigService
class CommonConfigTestCase(TestCase):
def setUp(self):
CommonConfig.objects.create(key='start_year', value='2023')
CommonConfig.objects.create(key='end_year', value='2024')
CommonConfig.objects.create(key='start_month', value='1')
CommonConfig.objects.create(key='end_month', value='10')
def test_common_config(self):
cfg = ConfigService()
self.assertEqual(cfg.start_year, '2023')
self.assertEqual(cfg.end_year, '2024')
self.assertEqual(cfg.start_month, '1')
self.assertEqual(cfg.end_month, '10')
Integration Testing
Integration testing involves simulating HTTP requests to access endpoints and check their functionality.
Django's TestCase class comes with a built-in client attribute, making it convenient to send requests to endpoints.
Generally, endpoints require authentication to use, and client thoughtfully integrates Django's authentication and authorization system—you can just use login directly.
Note that a temporary database is automatically created during testing, so you need to add users first.
After that, you can use methods like self.client.get and self.client.post to test the endpoints.
from django.test import TestCase
from django.shortcuts import reverse
from django.contrib.auth.models import User
from rest_framework import status
class DashboardTests(TestCase):
def setUp(self):
User.objects.create_user(username='user', password='pwd')
self.client.login(username="user", password="pwd")
def test_overview(self):
resp = self.client.get(reverse('dashboard:overview'), {'grant_year': 2023, 'grant_month': 2})
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.json()['code'], status.HTTP_200_OK)
def test_monthly_data(self):
resp = self.client.get(reverse('dashboard:monthly_data'), {'grant_year': 2023, 'grant_month': 2})
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.json()['code'], status.HTTP_200_OK)
def test_county_data(self):
resp = self.client.get(reverse('dashboard:county_data'), {'grant_year': 2023, 'grant_month': 2})
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.json()['code'], status.HTTP_200_OK)
Running Tests
After writing tests
Use the command to run the tests
python manage.py test
For other parameters, please refer to the official documentation.
However, when running tests, it’s not as satisfying as in AspNetCore; there’s no excitement of seeing each endpoint light up one after another.
In Django, you can only see the test results—how many tests passed—and if there are errors, you’ll see the Traceback information.
$ python .\manage.py test
Found 5 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.....
----------------------------------------------------------------------
Ran 5 tests in 0.831s
OK
Destroying test database for alias 'default'...
Summary
Django still feels familiar, and it just works.
References
I didn't realize Django had reached version 5.0 before I knew it…
_EOF_
Django's Unit Tests and Integration Tests
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
Django过时了吗?从ASGI到AI时代的思考
Django过时?技术趋势变了而已,异步支持短板在AI时代更明显。生态工程化再稳,也挡不住轻量框架和ASGI的冲击。独立开发者该考虑新选择了?
2025-09-12 · Django · 约 1 分钟
分享一些2026年有意思的现代化Django生态组件
2026年了,若还在忍受石器时代的模板和冗余CRUD,趁早转行。Cotton和Inertia总算给Django这老古董续了命,Ninja这类工具已是开发及格线。看一眼这些现代化组件吧,别再用那些自我感动的低效代码来折磨审美了。
2026-01-21 · Django · 约 1 分钟
如何优雅地在Django项目里生成不重复的ID?
在生成唯一 ID 这件事上,Django 和其他后端框架没啥不同的,思路都是类似的,只不过可以借助 Python 生态偷懒一下…
2024-12-08 · Django · 约 1 分钟
Django的信号机制入门
概念 django自带一套信号机制来帮助我们在框架的不同位置之间传递信息。也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(signals)发送给一组接受者(receivers)。 (感觉就很像Qt的信号与槽机制) 信号系统包含以下三要素: 发送者-信号的发出方 信号-信号本身 接收者-信号的接受者 Django内置了一整套信号,下面是一些比较常用的: 在
2022-08-26 · Django · 约 1 分钟
讨论
共 0 条加载评论中…