使用 Django 进行测试驱动开发
本文详细介绍测试驱动开发(TDD)的核心概念与优势,并通过 Django 框架演示完整的 TDD 工作流。内容涵盖如何编写测试用例、实现业务逻辑及运行测试,帮助开发者理解如何通过先写测试来明确需求、提高代码质量并控制开发范围。
测试驱动开发(TDD)是一种软件开发方法论,其核心流程是先编写测试用例,随后编写代码以通过测试。具体实施步骤如下:
- 第一步是编写测试用例。
- 第二步是编写代码,确保代码逻辑满足测试用例中定义的需求。
- 第三步是运行测试用例。
- 若测试通过,表明代码已正确实现测试用例所定义的功能需求。
- 若测试未通过,则需对代码进行重构或修改,直至测试通过。
- 重复上述步骤,直到所有测试用例均通过。
传统开发模式通常先写代码后写测试,因此 TDD 看似反直觉。然而,采用 TDD 有以下主要原因:
- TDD 可视为通过测试用例来明确需求。在编写源代码时,重点在于满足这些既定要求。当测试最终通过时,开发者可以确信需求已得到满足。这种聚焦有助于避免需求蔓延(范围蔓延)。
- TDD 通过缩短开发周期来提升效率。每次仅解决测试用例中的个别需求,可最大限度地减少干扰。重大变更更容易被跟踪和修复,从而减少了调试工作量,提高了开发效率,使开发者能将更多时间投入核心开发工作。
- 由于编写测试时已考虑需求,测试代码往往更加明确且易于理解。因此,这些测试可作为代码库的优质文档。
- 先编写测试用例能确保源代码始终具备可测试性,并保证随着代码库的扩展,测试覆盖率维持在合理水平。
然而,TDD 并非适用于所有场景。以下情况不适合采用 TDD:
- 当需求不明确时,随着开发推进,需求可能会逐渐清晰。此时,最初编写的测试用例可能迅速过时。
- 当开发目的是验证概念(Proof of Concept)时,例如在黑客马拉松期间,测试通常不是优先事项。
理解 TDD 概念后,我们将使用 Django 框架演示 TDD 的具体实施过程(需 Python 3.7 及以上版本,Django 2.0 及以上版本)。
首先描述需求:我们需要实现一个单位换算的 Web 应用,支持厘米、米和英里之间的相互转换。Web 界面效果如图所示:


创建项目
首先,我们创建一个名为 convert 的项目:
pip install django
django-admin startproject converter
此时,Django 已自动生成 converter 目录及基础项目文件:
converter/
converter/
__init__.py
settings.py
urls.py
wsgi.py
manage.py
接着,进入 converter 目录,创建一个名为 length 的 app:
cd converter
python manage.py startapp length
此时你将看到如下目录结构:
converter/
converter/
__init__.py
settings.py
urls.py
wsgi.py
length/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
manage.py
配置 app
修改 converter/settings.py 文件,在 INSTALLED_APPS 列表中加入 'length':
INSTALLED_APPS = [
.
.
.
'length',
]
然后在 length 目录下新建 urls.py 文件,写入以下内容:
from django.urls import path
from length import views
app_name = 'length'
urlpatterns = [
path('convert/', views.convert, name='convert'),
]
最后在 converter/urls.py 中指向 length/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('length/', include('length.urls')),
]
至此,一个不含业务逻辑的项目结构已创建成功,接下来开始编写测试用例:
编写测试用例
在 length 目录下新建 tests.py 文件,写入以下内容:
from django.test import TestCase, Client
from django.urls import reverse
class TestLengthConversion(TestCase):
"""
This class contains tests that convert measurements from one
unit of measurement to another.
"""
def setUp(self):
"""
This method runs before the execution of each test case.
"""
self.client = Client()
self.url = reverse("length:convert")
def test_centimetre_to_metre_conversion(self):
"""
Tests conversion of centimetre measurements to metre.
"""
data = {
"input_unit": "centimetre",
"output_unit": "metre",
"input_value": 8096.894
}
response = self.client.get(self.url, data)
self.assertContains(response, 80.96894)
def test_centimetre_to_mile_conversion(self):
data = {
"input_unit": "centimetre",
"output_unit": "mile",
"input_value": round(985805791.3527409, 3)
}
response = self.client.get(self.url, data)
self.assertContains(response, 6125.5113)
上述代码包含两个测试用例,分别对应两个功能需求。test_centimetre_to_metre_conversion 用于测试厘米转米的功能,而 test_centimetre_to_mile_conversion 用于测试厘米转英里的功能。
编写代码
这与常规 Django 开发无异,首先编写 forms.py 文件,内容如下:
from django import forms
class LengthConverterForm(forms.Form):
MEASUREMENTS = (
('centimetre', '厘米'),
('metre', '米'),
('mile', '英里')
)
input_unit = forms.ChoiceField(choices=MEASUREMENTS)
input_value = forms.DecimalField(decimal_places=3)
output_unit = forms.ChoiceField(choices=MEASUREMENTS)
output_value = forms.DecimalField(decimal_places=3, required=False)
接着编写 HTML 模板,在 length 目录下新建 templates/length.html 文件,内容如下:
<html lang="en">
<head>
<title>Length Conversion</title>
</head>
<body>
<form action={% url "length:convert" %} method="get">
<div>
{{ form.input_unit }}
{{ form.input_value }}
</div>
<input type="submit" value="转换为:"/>
<div>
{{ form.output_unit }}
{{ form.output_value }}
</div>
</form>
</body>
</html>
然后编写核心的视图函数 views.py,内容如下:
from django.shortcuts import render
from length.forms import LengthConverterForm
convert_to_metre = {
"centimetre": 0.01,
"metre": 1.0,
"mile": 1609.34
}
convert_from_metre = {
"centimetre": 100,
"metre": 1.0,
"mile": 0.000621371
}
# Create your views here.
def convert(request):
form = LengthConverterForm()
if request.GET:
input_unit = request.GET['input_unit']
input_value = request.GET['input_value']
output_unit = request.GET['output_unit']
metres = convert_to_metre[input_unit] * float(input_value)
print(f"{metres = }, {input_value = }")
output_value = metres * convert_from_metre[output_unit]
data = {
"input_unit": input_unit,
"input_value": input_value,
"output_unit": output_unit,
"output_value": round(output_value,5)
}
form = LengthConverterForm(initial=data)
return render(
request, "length.html", context={"form": form})
return render(
request, "length.html", context={"form": form})
执行测试
执行测试无需启动 Django 的 runserver 服务:

若输出 OK,说明测试通过。此时启动 Django 服务:
python manage.py runserver
打开浏览器,访问 http://localhost:8000/length/convert/ 即可看到应用界面:

最后的话
本文介绍了测试驱动开发(TDD)的概念,并通过 TDD 方式创建了一个简单的 Django 长度转换应用程序。与传统开发相比,TDD 强调先编写测试用例,编码旨在使测试通过。这种方式有助于明确需求、缩短开发周期、实现增量可控开发、提高开发效率,并确保测试覆盖率。
到此这篇关于使用 Django 进行测试驱动开发的文章就介绍到这了,更多相关 Django 测试驱动开发内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









