pytest 其他測試系統(tǒng)-使用unittest基于pytest的測試

2022-03-29 17:26 更新

pytest 支持開箱即用地運行基于 Python 單元測試的測試。 它旨在利用現(xiàn)有的基于單元測試的測試套件將 pytest 用作測試運行器,并允許逐步調(diào)整測試套件以充分利用 pytest 的功能。

要使用 pytest 運行現(xiàn)有的 ?unittest樣式的測試套件,請鍵入:

pytest tests

pytest 將自動在 ?test_*.py? 或 ?*_test.py? 文件中收集 ?unittest.TestCase? 子類及其測試方法。

幾乎所有單元測試功能都受支持:

  • ?@unittest.skip style decorators?
  • ?setUp/tearDown?
  • ?setUpClass/tearDownClass?
  • ?setUpModule/tearDownModule?

到目前為止,pytest 不支持以下功能:

  • ?load_tests protocol?
  • ?subtests?

開箱即用的好處

通過使用 pytest 運行您的測試套件,您可以利用多個功能,在大多數(shù)情況下無需修改現(xiàn)有代碼:

  • 獲得更多信息的追溯
  • 標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤捕獲
  • 使用 ?-k? 和 ?-m? 標(biāo)志測試選擇選項
  • 在第一次(或 ?N? 次)失敗后停止
  • ?-pdb? 用于調(diào)試測試失敗的命令行選項
  • 使用 ?pytest-xdist? 插件將測試分發(fā)到多個 CPU
  • 使用簡單的斷言語句而不是 ?self.assert*? 函數(shù)(?unittest2pytest在這方面非常有幫助)

unittest.TestCase 子類中的 pytest 功能

以下 pytest 功能在 ?unittest.TestCase子類中工作:

  • ?Marks?: ?skip?, ?skipif?, ?xfail?
  • ?Auto-use fixtures?

以下 pytest 功能不起作用,并且由于設(shè)計理念不同,可能永遠(yuǎn)不會起作用:

  • ?Fixtures ?
  • ?Parametrization ?
  • ??Custom ?hooks?

第三方插件可能運行良好,也可能運行不佳,具體取決于插件和測試套件。

使用標(biāo)記將 pytest fixture混合到 unittest.TestCase 子類中

在pytest中運行unittest允許你在unittest中使用它的?fixture?機制。TestCase風(fēng)格測試。假設(shè)你已經(jīng)瀏覽了pytest ?fixture?的特性,讓我們開始一個集成了?pytest db_class fixture?的例子,設(shè)置一個類緩存的數(shù)據(jù)庫對象,然后從一個unittest風(fēng)格的測試引用它:

# content of conftest.py

# we define a fixture function below and it will be "used" by
# referencing its name from tests

import pytest


@pytest.fixture(scope="class")
def db_class(request):
    class DummyDB:
        pass

    # set a class attribute on the invoking test context
    request.cls.db = DummyDB()

這定義了一個?fixture?函數(shù)?db_class?,如果使用,則對每個測試類調(diào)用一次,并將類級別的?db?屬性設(shè)置為一個?DummyDB?實例。?fixture?函數(shù)通過接收一個特殊的請求對象來實現(xiàn)這一點,該請求對象提供對請求測試上下文的訪問,比如cls屬性,它表示使用?fixture?的類。這種體系結(jié)構(gòu)將?fixture?的編寫與實際的測試代碼分離開來,并允許通過最小引用(?fixture?名稱)重用?fixture?。我們來寫一個?unittest?。?TestCase?類使用我們的?fixture?定義:

# content of test_unittest_db.py

import unittest
import pytest


@pytest.mark.usefixtures("db_class")
class MyTest(unittest.TestCase):
    def test_method1(self):
        assert hasattr(self, "db")
        assert 0, self.db  # fail for demo purposes

    def test_method2(self):
        assert 0, self.db  # fail for demo purposes

?@pytest.mark.usefixtures("db_class")類裝飾器確保每個類調(diào)用一次 pytest ?fixture?函數(shù) ?db_class?,由于故意失敗的斷言語句,我們可以查看回溯中的self.db值:

$ pytest test_unittest_db.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 2 items

test_unittest_db.py FF                                               [100%]

================================= FAILURES =================================
___________________________ MyTest.test_method1 ____________________________

self = <test_unittest_db.MyTest testMethod=test_method1>

    def test_method1(self):
        assert hasattr(self, "db")
>       assert 0, self.db  # fail for demo purposes
E       AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E       assert 0

test_unittest_db.py:10: AssertionError
___________________________ MyTest.test_method2 ____________________________

self = <test_unittest_db.MyTest testMethod=test_method2>

    def test_method2(self):
>       assert 0, self.db  # fail for demo purposes
E       AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef0001>
E       assert 0

test_unittest_db.py:13: AssertionError
========================= short test summary info ==========================
FAILED test_unittest_db.py::MyTest::test_method1 - AssertionError: <conft...
FAILED test_unittest_db.py::MyTest::test_method2 - AssertionError: <conft...
============================ 2 failed in 0.12s =============================

這個默認(rèn)的 pytest 回溯顯示這兩個測試方法共享同一個 ?self.db? 實例,這是我們在上面編寫類范圍的?fixture函數(shù)時的意圖。

使用 autouse fixture和訪問其他fixture

雖然對于給定的測試,顯式聲明所需使用的?fixture?通常更好,但有時您可能希望在給定的上下文中自動使用?fixture?。畢竟,?unittest-setup?的傳統(tǒng)風(fēng)格要求使用這種隱式的?fixture?編寫,您可能已經(jīng)習(xí)慣或喜歡它了。

您可以使用?@pytest.fixture(autuse =True)?標(biāo)記?fixture?函數(shù),并在希望使用它的上下文中定義?fixture?函數(shù)。讓我們看一下?initdir fixture?,它使?TestCase?類的所有測試方法在一個帶有預(yù)先初始化的?samplefile.ini?的臨時目錄中執(zhí)行。我們的?initdir fixture?本身使用pytest內(nèi)置的?tmp_path fixture?來委托創(chuàng)建每個測試的臨時目錄:

# content of test_unittest_cleandir.py
import os
import pytest
import unittest


class MyTest(unittest.TestCase):
    @pytest.fixture(autouse=True)
    def initdir(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)  # change to pytest-provided temporary directory
        tmp_path.joinpath("samplefile.ini").write_text("# testdata")

    def test_method(self):
        with open("samplefile.ini") as f:
            s = f.read()
        assert "testdata" in s

由于有?autuse?標(biāo)志,?initdir fixture?函數(shù)將用于定義它的類的所有方法。這是在類上使用?@pytest.mark.usefixture ("initdir")?標(biāo)記的快捷方式,就像前面的例子一樣。

運行這個測試模塊:

$ pytest -q test_unittest_cleandir.py
.                                                                    [100%]
1 passed in 0.12s

上述測試通過,因為?initdir fixture?函數(shù)在?test_method?之前被執(zhí)行。

?unittest.TestCase? 方法不能直接接收?fixture?參數(shù)作為實現(xiàn),這可能會影響運行通用 ?unittest.TestCase? 測試套件的能力。

上面的 ?usefixtures ?和 ?autouse ?示例應(yīng)該有助于將 pytest ?fixture?混合到單元測試套件中。

您還可以逐漸從 ?unittest.TestCase? 的子類化轉(zhuǎn)移到普通斷言,然后開始逐步受益于完整的 pytest 功能集。

由于兩個框架之間的架構(gòu)差異,基于單元測試的設(shè)置和拆卸是在測試的調(diào)用階段執(zhí)行的,而不是在pytest的標(biāo)準(zhǔn)設(shè)置和拆卸階段。在某些情況下,理解這一點很重要,特別是在推理錯誤時。例如,如果基于單元測試的套件在安裝期間出現(xiàn)錯誤,pytest在安裝階段報告沒有錯誤,而是在調(diào)用期間引發(fā)錯誤。


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號