<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 Django 進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā)

          共 5240字,需瀏覽 11分鐘

           ·

          2021-11-27 21:21

          所謂測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(TDD),就是先編寫(xiě)測(cè)試用例,然后編寫(xiě)代碼來(lái)滿足測(cè)試用例,具體包含以下步驟:

          1. 編寫(xiě)測(cè)試用例。
          2. 編寫(xiě)代碼滿足測(cè)試用例中的需求。
          3. 運(yùn)行測(cè)試用例。
          4. 如果通過(guò),說(shuō)明代碼滿足了測(cè)試用例所定義的需求。
          5. 如果未通過(guò),則需要重構(gòu)代碼,直到通過(guò)。
          6. 重復(fù)以上步驟,直到通過(guò)全部的測(cè)試用例。

          通常情況下,我們都是先寫(xiě)代碼,然后編寫(xiě)測(cè)試用例,因此測(cè)試驅(qū)動(dòng)開(kāi)發(fā)是反直覺(jué)的,那為什么還要這么做呢?基于以下幾點(diǎn)原因:

          1. TDD 可以被認(rèn)為是根據(jù)測(cè)試用例來(lái)說(shuō)明需求。此后編寫(xiě)源代碼,重點(diǎn)是滿足這些要求。當(dāng)測(cè)試最終通過(guò)時(shí),你可以確信已滿足要求。這種專注可以幫助開(kāi)發(fā)人員避免范圍蔓延。
          2. TDD 可以通過(guò)較短的開(kāi)發(fā)周期提高開(kāi)發(fā)效率。一次解決測(cè)試用例中的個(gè)別可以最大限度地減少干擾因素。重大更改將更容易跟蹤和解決。減少了調(diào)試工作,提高了效率,并且將更多時(shí)間花在開(kāi)發(fā)上。
          3. 編寫(xiě)測(cè)試時(shí)考慮到了需求。正因?yàn)槿绱?,它們更有可能被?xiě)成明確的,可以理解的。這樣的測(cè)試可以作為代碼庫(kù)的優(yōu)質(zhì)文檔。
          4. 先編寫(xiě)測(cè)試用例可確保您的源代碼始終具有可測(cè)試性,它還保證隨著代碼庫(kù)的增長(zhǎng),測(cè)試覆蓋率始終保持在合理的百分比。

          然而,測(cè)試驅(qū)動(dòng)開(kāi)發(fā)也不是銀彈,以下情形并不適合測(cè)試驅(qū)動(dòng)開(kāi)發(fā):

          1. 當(dāng)需求不明確時(shí),有時(shí)續(xù)期會(huì)隨著開(kāi)發(fā)的進(jìn)行而逐漸明確,在這種情況下最初編寫(xiě)的任何測(cè)試可能會(huì)過(guò)時(shí)。
          2. 開(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 界面如圖所示:

          1、創(chuàng)建項(xiàng)目

          完整代碼公眾號(hào)回復(fù)【TDD】獲取。

          首先,我們創(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

          2、配置 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è)試用例:

          3、編寫(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)英里的需求。

          4、編寫(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?Conversiontitle>
          ??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})

          5、執(zhí)行測(cè)試

          執(zhí)行測(cè)試并不需要啟動(dòng) django 的 runserver:

          出現(xiàn) OK 說(shuō)明測(cè)試通過(guò),啟動(dòng) django:

          python?manage.py?runserver

          打開(kāi)瀏覽器,訪問(wèn) http://localhost:8000/length/convert/ 即可看到界面:

          完整代碼公眾號(hào)回復(fù)【TDD】獲取。

          最后的話

          本文分享了什么是測(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è)試用例,其他沒(méi)啥區(qū)別,這樣的方式可以使得需求更明確,開(kāi)發(fā)周期更短,增量可控,提高開(kāi)發(fā)效率,保證測(cè)試覆蓋率。

          如果覺(jué)得有用,還請(qǐng)點(diǎn)贊、在看、關(guān)注支持,感謝!

          留言討論


          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青青操青青操在线视频免费 | 婷婷色综合五月天 | 成人片777| 日韩国产一区二区 | 日韩电影A片在线 |