Django4.0 測試-編寫并運行測試

2022-03-17 14:01 更新

編寫測試

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

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"')

當你 運行你的測試 時,測試工具的默認行為是在任何名字以 ?test? 開頭的文件中找到所有的測試用例(也就是 ?unittest.TestCase? 的子類),從這些測試用例中自動構建一個測試套件,然后運行該套件。

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

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

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

運行測試

編寫完測試后,使用項目的 ?manage.py? 實用程序的 ?test ?命令運行它們:

$ ./manage.py test

測試發(fā)現(xiàn)是基于 unittest 模塊的 內(nèi)建測試發(fā)現(xiàn)。默認情況下,這將發(fā)現(xiàn)當前工作目錄下任何名為“test*.py”的文件中的測試。
你可以通過向 ?./manage.py test? 提供任意數(shù)量的測試標簽來指定要運行的特定測試。每個測試標簽可以是指向包、模塊、TestCase 子類或測試方法的點分隔 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)該目錄下的測試:

$ ./manage.py test animals/

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

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

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

測試數(shù)據(jù)庫

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

執(zhí)行測試的順序

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

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

回滾模擬

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

其他測試條件

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

了解測試輸出

當你運行測試時,你會看到一些消息,因為測試運行器正在做準備。你可以通過命令行上的 ?verbosity ?選項來控制這些消息的詳細程度:

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

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

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

OK

但是,如果有測試失敗,你會看到關于哪些測試失敗的完整細節(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)

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

加快測試

并行運行測試

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

密碼哈希

默認密碼哈希器在設計上相當慢。 如果要在測試中對許多用戶進行身份驗證,則可能需要使用自定義設置文件,并將 ?PASSWORD_HASHERS ?設置為更快的哈希算法:

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

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

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

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


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號