使用 Django 進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā)
所謂測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD),就是先編寫(xiě)測(cè)試用例,然后編寫(xiě)代碼來(lái)滿(mǎn)足測(cè)試用例,具體包含以下步驟:
- 編寫(xiě)測(cè)試用例。
- 編寫(xiě)代碼滿(mǎn)足測(cè)試用例中的需求。
- 運(yùn)行測(cè)試用例。
- 如果通過(guò),說(shuō)明代碼滿(mǎn)足了測(cè)試用例所定義的需求。
- 如果未通過(guò),則需要重構(gòu)代碼,直到通過(guò)。
- 重復(fù)以上步驟,直到通過(guò)全部的測(cè)試用例。
通常情況下,我們都是先寫(xiě)代碼,然后編寫(xiě)測(cè)試用例,因此測(cè)試驅(qū)動(dòng)開(kāi)發(fā)是反直覺(jué)的,那為什么還要這么做呢?基于以下幾點(diǎn)原因:
- TDD 可以被認(rèn)為是根據(jù)測(cè)試用例來(lái)說(shuō)明需求。此后編寫(xiě)源代碼,重點(diǎn)是滿(mǎn)足這些要求。當(dāng)測(cè)試最終通過(guò)時(shí),你可以確信已滿(mǎn)足要求。這種專(zhuān)注可以幫助開(kāi)發(fā)人員避免范圍蔓延。
- TDD 可以通過(guò)較短的開(kāi)發(fā)周期提高開(kāi)發(fā)效率。一次解決測(cè)試用例中的個(gè)別需求可以最大限度地減少干擾因素。重大更改將更容易跟蹤和解決,減少了調(diào)試工作,提高了效率,并且將更多時(shí)間花在開(kāi)發(fā)上。
- 編寫(xiě)測(cè)試時(shí)考慮到了需求。正因?yàn)槿绱?,它們更有可能被?xiě)成明確的,可以理解的。這樣的測(cè)試可以作為代碼庫(kù)的優(yōu)質(zhì)文檔。
- 先編寫(xiě)測(cè)試用例可確保您的源代碼始終具有可測(cè)試性,它還保證隨著代碼庫(kù)的增長(zhǎng),測(cè)試覆蓋率始終保持在合理的百分比。
然而,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)也不是銀彈,以下情形并不適合測(cè)試驅(qū)動(dòng)開(kāi)發(fā):
- 當(dāng)需求不明確時(shí),有時(shí)續(xù)期會(huì)隨著開(kāi)發(fā)的進(jìn)行而逐漸明確,在這種情況下最初編寫(xiě)的任何測(cè)試可能會(huì)過(guò)時(shí)。
- 開(kāi)發(fā)的目的是為了證明某一概念時(shí)——例如在黑客馬拉松期間,測(cè)試通常不是優(yōu)先事項(xiàng)。
了解了測(cè)試驅(qū)動(dòng)開(kāi)發(fā)之后,我們用 Django 來(lái)演示一下測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的過(guò)程。(Python 3.7 以上,Django 2.0 以上)
首先描述需求,我們要實(shí)現(xiàn)這樣一個(gè)單位換算功能的 Web 應(yīng)用,可以在厘米、米、英里直接互相轉(zhuǎn)換,Web 界面如圖所示:


創(chuàng)建項(xiàng)目
首先,我們創(chuàng)建一個(gè)名字叫 convert 的項(xiàng)目:
pip install django django-admin startproject converter
此時(shí) Django 已經(jīng)為我們生成了 converter 目錄及基本的項(xiàng)目文件:
converter/
converter/
__init__.py
settings.py
urls.py
wsgi.py
manage.py
然后,進(jìn)入 converter 目錄,創(chuàng)建一個(gè)名字叫 length 的 app:
cd converter python manage.py startapp length
然后你會(huì)看到這樣的目錄結(jié)構(gòu):
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 里加入 lengh :
INSTALLED_APPS = [
.
.
.
'length',
]
然后在 length 目錄下新建 urls.py,寫(xiě)入以下內(nèi)容:
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')),
]
這樣一個(gè)沒(méi)有任何業(yè)務(wù)邏輯的項(xiàng)目就創(chuàng)建成功了,接下來(lái)編寫(xiě)測(cè)試用例:
編寫(xiě)測(cè)試用例
在 lengh 目錄下新建 tests.py,寫(xiě)入以下內(nèi)容:
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)
上述代碼有兩個(gè)測(cè)試用例,分別代表兩個(gè)需求。test_centimetre_to_metre_conversion 代表厘米轉(zhuǎn)米的需求,而 test_centimetre_to_mile_conversion 代表厘米轉(zhuǎn)英里的需求。
編寫(xiě)代碼
這和 Django 開(kāi)發(fā)沒(méi)什么兩樣,先編寫(xiě)一個(gè) forms.py,內(nèi)容如下:
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)
然后編寫(xiě) html,在 length 目錄下新建 templates/length.html,內(nèi)容如下:
<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="轉(zhuǎn)換為:"/>
<div>
{{ form.output_unit }}
{{ form.output_value }}
</div>
</form>
</body>
</html>
然后編寫(xiě)最重要的視圖函數(shù) views.py,內(nèi)容如下:
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})
執(zhí)行測(cè)試
執(zhí)行策四并不需要啟動(dòng) django 的 runserver:

出現(xiàn) OK 說(shuō)明測(cè)試通過(guò),啟動(dòng) django:
python manage.py runserver
打開(kāi)瀏覽器,訪(fǎng)問(wèn) http://localhost:8000/length/convert/ 即可看到界面:

最后的話(huà)
本文分享了什么是測(cè)試驅(qū)動(dòng)開(kāi)發(fā),并用測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的方式 創(chuàng)建了一個(gè)簡(jiǎn)單的 Django 應(yīng)用程序,用于長(zhǎng)度轉(zhuǎn)換。和一般開(kāi)發(fā)的區(qū)別就是先寫(xiě)好測(cè)試用例,編碼是為了讓測(cè)試用例通過(guò),這樣的方式可以使得需求更明確,開(kāi)發(fā)周期更短,增量可控,提高開(kāi)發(fā)效率,保證測(cè)試覆蓋率。
到此這篇關(guān)于使用 Django 進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的文章就介紹到這了,更多相關(guān)Django測(cè)試驅(qū)動(dòng)開(kāi)發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python 添加用戶(hù)設(shè)置密碼并發(fā)郵件給root用戶(hù)
這篇文章主要介紹了python 添加用戶(hù)設(shè)置密碼并發(fā)郵件給root用戶(hù)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
Python flask框架定時(shí)任務(wù)apscheduler應(yīng)用介紹
Flask是Python社區(qū)非常流行的一個(gè)Web開(kāi)發(fā)框架,本文將嘗試將介紹APScheduler應(yīng)用于Flask之中實(shí)現(xiàn)定時(shí)任務(wù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10
Seaborn數(shù)據(jù)分析NBA球員信息數(shù)據(jù)集
這篇文章主要為大家介紹了Seaborn數(shù)據(jù)分析處理NBA球員信息數(shù)據(jù)集案例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
Python后臺(tái)開(kāi)發(fā)Django會(huì)話(huà)控制的實(shí)現(xiàn)
這篇文章主要介紹了Python后臺(tái)開(kāi)發(fā)Django會(huì)話(huà)控制的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Pycharm中import torch報(bào)錯(cuò)的快速解決方法
這篇文章主要介紹了Pycharm中import torch報(bào)錯(cuò)的快速解決方法,很多朋友容易碰到這個(gè)問(wèn)題,今天小編特此把解決方案分享到腳本之家平臺(tái)供大家參考,需要的朋友可以參考下2020-03-03
使用python讀取csv文件快速插入數(shù)據(jù)庫(kù)的實(shí)例
今天小編就為大家分享一篇使用python讀取csv文件快速插入數(shù)據(jù)庫(kù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
K-means聚類(lèi)算法介紹與利用python實(shí)現(xiàn)的代碼示例
K-means聚類(lèi)算法(事先數(shù)據(jù)并沒(méi)有類(lèi)別之分!所有的數(shù)據(jù)都是一樣的)是我們大家應(yīng)該都聽(tīng)過(guò)的一種算法,下面這篇文章主要給大家介紹了關(guān)于K-means聚類(lèi)算法的基礎(chǔ)知識(shí)與利用python如何實(shí)現(xiàn)該算法的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-11-11

