Django4.0 測(cè)試-編寫(xiě)并運(yùn)行測(cè)試

2022-03-17 14:01 更新

編寫(xiě)測(cè)試

Django 的單元測(cè)試采用 Python 的標(biāo)準(zhǔn)模塊: ?unittest?。該模塊以類的形式定義測(cè)試。
下面是一個(gè)例子,它是 ?django.test.TestCase? 的子類,同時(shí)父類也是 ?unittest.TestCase? 的子類,在事務(wù)內(nèi)部運(yùn)行每個(gè)測(cè)試以提供隔離:

from django.test import TestCase
from myapp.models import Animal

class AnimalTestCase(TestCase):
    def setUp(self):
        Animal.objects.create(name="lion", sound="roar")
        Animal.objects.create(name="cat", sound="meow")

    def test_animals_can_speak(self):
        """Animals that can speak are correctly identified"""
        lion = Animal.objects.get(name="lion")
        cat = Animal.objects.get(name="cat")
        self.assertEqual(lion.speak(), 'The lion says "roar"')
        self.assertEqual(cat.speak(), 'The cat says "meow"')

當(dāng)你 運(yùn)行你的測(cè)試 時(shí),測(cè)試工具的默認(rèn)行為是在任何名字以 ?test? 開(kāi)頭的文件中找到所有的測(cè)試用例(也就是 ?unittest.TestCase? 的子類),從這些測(cè)試用例中自動(dòng)構(gòu)建一個(gè)測(cè)試套件,然后運(yùn)行該套件。

默認(rèn)的 ?startapp ?會(huì)在新的應(yīng)用程序中創(chuàng)建一個(gè) ?tests.py? 文件。如果你只有幾個(gè)測(cè)試,這可能是好的,但隨著你的測(cè)試套件的增長(zhǎng),你可能會(huì)想把它重組為一個(gè)測(cè)試包,這樣你就可以把你的測(cè)試分成不同的子模塊,如 ?test_models.py?、?test_views.py?、?test_forms.py? 等。你可以自由選擇任何你喜歡的組織方案。

如果您的測(cè)試依賴于數(shù)據(jù)庫(kù)訪問(wèn),例如創(chuàng)建或查詢模型,請(qǐng)確保將您的測(cè)試類創(chuàng)建為 ?django.test.TestCase? 的子類,而不是 ?unittest.TestCase?。

使用 ?unittest.TestCase? 避免了在事務(wù)中運(yùn)行每個(gè)測(cè)試并刷新數(shù)據(jù)庫(kù)的成本,但是如果您的測(cè)試與數(shù)據(jù)庫(kù)交互,它們的行為將根據(jù)測(cè)試運(yùn)行器執(zhí)行它們的順序而有所不同。 這可能導(dǎo)致單元測(cè)試在單獨(dú)運(yùn)行時(shí)通過(guò),但在套件中運(yùn)行時(shí)失敗。

運(yùn)行測(cè)試

編寫(xiě)完測(cè)試后,使用項(xiàng)目的 ?manage.py? 實(shí)用程序的 ?test ?命令運(yùn)行它們:

$ ./manage.py test

測(cè)試發(fā)現(xiàn)是基于 unittest 模塊的 內(nèi)建測(cè)試發(fā)現(xiàn)。默認(rèn)情況下,這將發(fā)現(xiàn)當(dāng)前工作目錄下任何名為“test*.py”的文件中的測(cè)試。
你可以通過(guò)向 ?./manage.py test? 提供任意數(shù)量的測(cè)試標(biāo)簽來(lái)指定要運(yùn)行的特定測(cè)試。每個(gè)測(cè)試標(biāo)簽可以是指向包、模塊、TestCase 子類或測(cè)試方法的點(diǎn)分隔 Python 路徑。例如:

# Run all the tests in the animals.tests module
$ ./manage.py test animals.tests

# Run all the tests found within the 'animals' package
$ ./manage.py test animals

# Run just one test case
$ ./manage.py test animals.tests.AnimalTestCase

# Run just one test method
$ ./manage.py test animals.tests.AnimalTestCase.test_animals_can_speak

你還可以提供目錄路徑,以發(fā)現(xiàn)該目錄下的測(cè)試:

$ ./manage.py test animals/

如果你的測(cè)試文件的命名與 ?test*.py? 模式不同,你可以使用 ?-p? (或 ?--pattern?)選項(xiàng)指定一個(gè)自定義文件名模式匹配:

$ ./manage.py test --pattern="tests_*.py"

如果你在測(cè)試運(yùn)行時(shí)按 ?Ctrl+C?,測(cè)試運(yùn)行器將等待當(dāng)前運(yùn)行的測(cè)試完成,然后優(yōu)雅地退出。在優(yōu)雅退出過(guò)程中,測(cè)試運(yùn)行器將輸出任何測(cè)試失敗的細(xì)節(jié),報(bào)告運(yùn)行了多少次測(cè)試,遇到了多少次錯(cuò)誤和失敗,并像往常一樣銷毀任何測(cè)試數(shù)據(jù)庫(kù)。因此,如果你忘記了傳入 ?--failfast? 選項(xiàng),注意到一些測(cè)試意外地失敗了,并且想在不等待整個(gè)測(cè)試運(yùn)行完成的情況下獲得失敗的細(xì)節(jié),那么按下 ?Ctrl+C? 就會(huì)非常有用。
如果你不想等待當(dāng)前正在進(jìn)行的測(cè)試結(jié)束,你可以按兩次 ?Ctrl+C?,測(cè)試運(yùn)行將立即停止,但不會(huì)優(yōu)雅地停止。不會(huì)報(bào)告中斷前運(yùn)行的測(cè)試細(xì)節(jié),也不會(huì)銷毀運(yùn)行中創(chuàng)建的任何測(cè)試數(shù)據(jù)庫(kù)。

測(cè)試數(shù)據(jù)庫(kù)

需要數(shù)據(jù)庫(kù)的測(cè)試(即模型測(cè)試)將不會(huì)使用“實(shí)際”(生產(chǎn))數(shù)據(jù)庫(kù)。 將為測(cè)試創(chuàng)建單獨(dú)的空白數(shù)據(jù)庫(kù)。
無(wú)論測(cè)試是通過(guò)還是失敗,當(dāng)所有測(cè)試執(zhí)行完畢后,測(cè)試數(shù)據(jù)庫(kù)都會(huì)被銷毀。
你可以通過(guò)使用 ?test --keepdb? 選項(xiàng)來(lái)防止測(cè)試數(shù)據(jù)庫(kù)被破壞。 這將在兩次運(yùn)行之間保留測(cè)試數(shù)據(jù)庫(kù)。 如果數(shù)據(jù)庫(kù)不存在,將首先創(chuàng)建它。 任何遷移都將被應(yīng)用,以使其保持最新?tīng)顟B(tài)。
如上一節(jié)所述,如果測(cè)試運(yùn)行被強(qiáng)行中斷,測(cè)試數(shù)據(jù)庫(kù)可能不會(huì)被銷毀。在下一次運(yùn)行時(shí),你會(huì)被問(wèn)到是要重新使用還是銷毀數(shù)據(jù)庫(kù)。使用 ?test --noinput? 選項(xiàng)禁止顯示該提示并自動(dòng)銷毀數(shù)據(jù)庫(kù)。 例如,在持續(xù)集成服務(wù)器上運(yùn)行測(cè)試時(shí)這很有用,該測(cè)試可能會(huì)因超時(shí)而中斷。
默認(rèn)的測(cè)試數(shù)據(jù)庫(kù)名稱是通過(guò)在 DATABASES 中每個(gè) ?NAME ?的值前加上 ?test_? 來(lái)創(chuàng)建的。當(dāng)使用 SQLite時(shí),默認(rèn)情況下測(cè)試將使用內(nèi)存數(shù)據(jù)庫(kù)(即數(shù)據(jù)庫(kù)將在內(nèi)存中創(chuàng)建,完全繞開(kāi)文件系統(tǒng)!)。DATABASES 中的 ?TEST ?字典提供了許多設(shè)置來(lái)配置你的測(cè)試數(shù)據(jù)庫(kù)。例如,如果你想使用不同的數(shù)據(jù)庫(kù)名稱,給 DATABASES 中的每個(gè)數(shù)據(jù)庫(kù)在 ?TEST ?字典中指定 ?NAME?。
在 PostgreSQL 上,?USER ?也需要對(duì)內(nèi)置的 postgres 數(shù)據(jù)庫(kù)進(jìn)行讀取訪問(wèn)。
除了使用單獨(dú)的數(shù)據(jù)庫(kù)外,測(cè)試運(yùn)行器還將使用你在配置文件中的所有相同的數(shù)據(jù)庫(kù)設(shè)置: ?ENGINE?、?USER?、?HOST ?等。測(cè)試數(shù)據(jù)庫(kù)是由 ?USER ?指定的用戶創(chuàng)建的,所以你需要確保給定的用戶賬戶有足夠的權(quán)限在系統(tǒng)上創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)。
為了對(duì)測(cè)試數(shù)據(jù)庫(kù)的字符編碼進(jìn)行精細(xì)控制,請(qǐng)使用 ?CHARSET ?TEST 選項(xiàng)。如果你使用的是 MySQL,你也可以使用 ?COLLATION ?選項(xiàng)來(lái)控制測(cè)試數(shù)據(jù)庫(kù)使用的特定字符序。
如果使用 SQLite 內(nèi)存數(shù)據(jù)庫(kù),啟用了 共享緩存,你就可以編寫(xiě)線程之間共享數(shù)據(jù)庫(kù)的測(cè)試。

執(zhí)行測(cè)試的順序

為了保證所有的 ?TestCase ?代碼都從干凈的數(shù)據(jù)庫(kù)開(kāi)始,Django 測(cè)試運(yùn)行器以如下方式重新排序測(cè)試:

  • 所有 ?TestCase ?的子類首先運(yùn)行。
  • 然后,所有其他基于Django的測(cè)試(基于 ?SimpleTestCase ?的測(cè)試用例,包括 ?TransactionTestCase?)都會(huì)被運(yùn)行,它們之間不保證也不強(qiáng)制執(zhí)行特定的順序。
  • 然后運(yùn)行任何其他的 ?unittest.TestCase? 測(cè)試(包括 doctests),這些測(cè)試可能會(huì)改變數(shù)據(jù)庫(kù)而不將其恢復(fù)到原始狀態(tài)。

回滾模擬

任何在遷移中加載的初始數(shù)據(jù)將只能在 ?TestCase ?測(cè)試中使用,而不能在 ?TransactionTestCase ?測(cè)試中使用,此外,只有在支持事務(wù)的后端(最重要的例外是 MyISAM)上才能使用。對(duì)于依賴 ?TransactionTestCase ?的測(cè)試也是如此,比如 ?LiveServerTestCase ?和 ?StaticLiveServerTestCase?。
Django 可以通過(guò)在 ?TestCase ?或 ?TransactionTestCase ?中設(shè)置 ?serialized_rollback ?選項(xiàng)為 ?True ?來(lái)為你重新加載每個(gè)測(cè)試用例的數(shù)據(jù),但請(qǐng)注意,這將使測(cè)試套件的速度降低約 3 倍。
第三方應(yīng)用程序或那些針對(duì) MyISAM 開(kāi)發(fā)的應(yīng)用程序?qū)⑿枰O(shè)置這個(gè)功能;但是,一般來(lái)說(shuō),你應(yīng)該針對(duì)事務(wù)性數(shù)據(jù)庫(kù)開(kāi)發(fā)你自己的項(xiàng)目,并在大多數(shù)測(cè)試中使用 ?TestCase?,因此不需要這個(gè)設(shè)置。
初始序列化通常是非??斓?,但如果你希望從這個(gè)過(guò)程中排除一些應(yīng)用程序(并稍微加快測(cè)試運(yùn)行速度),你可以將這些應(yīng)用程序添加到 ?TEST_NON_SERIALIZED_APPS?。
為了防止序列化數(shù)據(jù)被加載兩次,設(shè)置 ?serialized_rollback=True? 在刷新測(cè)試數(shù)據(jù)庫(kù)時(shí)禁用 ?post_migrate ?信號(hào)。

其他測(cè)試條件

無(wú)論配置文件中的 ?DEBUG ?設(shè)置值是多少,所有的 Django 測(cè)試都以 ?DEBUG=False? 運(yùn)行。這是為了確保你的代碼觀察到的輸出與生產(chǎn)環(huán)境下的輸出一致。
每次測(cè)試后都不會(huì)清除緩存,如果在生產(chǎn)環(huán)境中運(yùn)行測(cè)試,則運(yùn)行 ?manage.py test fooapp ?可以將測(cè)試中的數(shù)據(jù)插入實(shí)時(shí)系統(tǒng)的緩存中,因?yàn)榕c數(shù)據(jù)庫(kù)不同的是,沒(méi)有使用單獨(dú)的測(cè)試緩存。這種行為在未來(lái)可能改變。

了解測(cè)試輸出

當(dāng)你運(yùn)行測(cè)試時(shí),你會(huì)看到一些消息,因?yàn)闇y(cè)試運(yùn)行器正在做準(zhǔn)備。你可以通過(guò)命令行上的 ?verbosity ?選項(xiàng)來(lái)控制這些消息的詳細(xì)程度:

Creating test database...
Creating table myapp_animal
Creating table myapp_mineral

這告訴你測(cè)試運(yùn)行程序正在創(chuàng)建測(cè)試數(shù)據(jù)庫(kù),如上一節(jié)所述。
創(chuàng)建測(cè)試數(shù)據(jù)庫(kù)后,Django 將運(yùn)行你的測(cè)試。 如果一切順利,你會(huì)看到類似以下內(nèi)容的信息:

----------------------------------------------------------------------
Ran 22 tests in 0.221s

OK

但是,如果有測(cè)試失敗,你會(huì)看到關(guān)于哪些測(cè)試失敗的完整細(xì)節(jié):

======================================================================
FAIL: test_was_published_recently_with_future_poll (polls.tests.PollMethodTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/dev/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_poll
    self.assertIs(future_poll.was_published_recently(), False)
AssertionError: True is not False

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (failures=1)

對(duì)這個(gè)錯(cuò)誤輸出的完整解釋超出了本文的范圍,但它非常直觀。
請(qǐng)注意,對(duì)于任何數(shù)量的失敗和錯(cuò)誤測(cè)試,?test-runner? 腳本的返回碼均為 1。 如果所有測(cè)試均通過(guò),則返回碼為 0。如果你在 shell 腳本中使用 ?test-runner? 腳本,并且需要在該級(jí)別上測(cè)試成功或失敗,則此功能很有用。

加快測(cè)試

并行運(yùn)行測(cè)試

只要測(cè)試正確隔離,你就可以并行運(yùn)行它們以加快多核硬件的運(yùn)行速度。

密碼哈希

默認(rèn)密碼哈希器在設(shè)計(jì)上相當(dāng)慢。 如果要在測(cè)試中對(duì)許多用戶進(jìn)行身份驗(yàn)證,則可能需要使用自定義設(shè)置文件,并將 ?PASSWORD_HASHERS ?設(shè)置為更快的哈希算法:

PASSWORD_HASHERS = [
    'django.contrib.auth.hashers.MD5PasswordHasher',
]

不要忘記在 ?PASSWORD_HASHERS ?中包含在輔助工具中使用的任何哈希算法,如果有的話。

保留測(cè)試數(shù)據(jù)庫(kù)

?test --keepdb? 選項(xiàng)在兩次測(cè)試運(yùn)行之間保留測(cè)試數(shù)據(jù)庫(kù)。 它跳過(guò)了創(chuàng)建和銷毀操作,這可以大大減少運(yùn)行測(cè)試的時(shí)間。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)