pytest fixture-拆除fixture

2022-03-18 14:10 更新

在我們運行測試時,我們希望確保它們在自己完成之后進行清理,這樣它們就不會擾亂其他測試(也不會留下大量的測試數(shù)據(jù)來膨脹系統(tǒng))。pytest中的??fixture??提供了一個非常有用的拆卸系統(tǒng),它允許我們?yōu)槊總€??fixture??定義必要的特定步驟,以便在它們自己之后進行清理。

該系統(tǒng)可以利用在兩個方面。

1、yield fixtures

使用這些??fixture??,我們可以運行一些代碼并將一個對象傳回請求??fixture/test??,就像使用其他??fixture??一樣。唯一的區(qū)別是:

  • ??return??被換成了??yield??。
  • 該??fixture??的拆卸代碼位于生成之后。

一旦pytest為??fixture??確定了一個線性順序,它將運行每個??fixture??,直到它返回或產(chǎn)生,然后移動到列表中的下一個??fixture??來做同樣的事情。

測試完成后,pytest將返回??fixture??列表,但順序相反,獲取每個產(chǎn)生的??fixture??,并在其中運行??yield??語句之后的代碼。

作為一個簡單的例子,考慮這個基本的電子郵件模塊:

# content of emaillib.py
class MailAdminClient:
    def create_user(self):
        return MailUser()

    def delete_user(self, user):
        # do some cleanup
        pass


class MailUser:
    def __init__(self):
        self.inbox = []

    def send_email(self, email, other):
        other.inbox.append(email)

    def clear_mailbox(self):
        self.inbox.clear()


class Email:
    def __init__(self, subject, body):
        self.subject = subject
        self.body = body

假設(shè)我們想測試從一個用戶向另一個用戶發(fā)送電子郵件。我們必須首先創(chuàng)建每個用戶,然后從一個用戶向另一個用戶發(fā)送電子郵件,最后斷言另一個用戶在他們的收件箱中收到了這條消息。如果我們想在測試運行后進行清理,我們必須確保在刪除其他用戶之前清空該用戶的郵箱,否則系統(tǒng)可能會報錯。

這可能是這樣的:

# content of test_emaillib.py
import pytest

from emaillib import Email, MailAdminClient


@pytest.fixture
def mail_admin():
    return MailAdminClient()


@pytest.fixture
def sending_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)


@pytest.fixture
def receiving_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)


def test_email_received(sending_user, receiving_user):
    email = Email(subject="Hey!", body="How's it going?")
    sending_user.send_email(email, receiving_user)
    assert email in receiving_user.inbox

因為??receiving_user??是安裝期間運行的最后一個??fixture??,所以它是拆卸期間運行的第一個??fixture??。

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

處理yield fixture的錯誤

如果??yield fixture??在??yield??之前引發(fā)異常,pytest將不會嘗試在該??yield fixture??的??yield??語句之后運行拆卸代碼。但是,對于已經(jīng)為該測試成功運行的每個??fixture??, pytest仍然會像正常情況一樣試圖將它們刪除。

2、直接添加finalizers

雖然??yield fixture??被認為是更干凈和更直接的選項,但還有另一種選擇,即直接向測試的請求上下文對象添加??finalizer??函數(shù)。它帶來了與??yield fixture??類似的結(jié)果,但需要更多的細節(jié)。為了使用這種方法,我們必須在需要添加??teardown??代碼的??fixture??中請求請求上下文對象(就像我們請求另一個??fixture??一樣),然后將包含該??teardown??代碼的可調(diào)用對象傳遞給它的??addfinalizer??方法。但是,我們必須小心,因為pytest將在添加??finalizer??后運行該??finalizer??,即使該??fixture??在添加??finalizer??后引發(fā)異常。因此,為了確保我們不會在不需要的時候運行??finalizer??代碼,我們只會在??fixture??做了一些我們需要拆除的事情時添加??finalizer??。下面是使用??addfinalizer??方法的前一個例子:

# content of test_emaillib.py
import pytest

from emaillib import Email, MailAdminClient


@pytest.fixture
def mail_admin():
    return MailAdminClient()


@pytest.fixture
def sending_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)


@pytest.fixture
def receiving_user(mail_admin, request):
    user = mail_admin.create_user()

    def delete_user():
        mail_admin.delete_user(user)

    request.addfinalizer(delete_user)
    return user


@pytest.fixture
def email(sending_user, receiving_user, request):
    _email = Email(subject="Hey!", body="How's it going?")
    sending_user.send_email(_email, receiving_user)

    def empty_mailbox():
        receiving_user.clear_mailbox()

    request.addfinalizer(empty_mailbox)
    return _email


def test_email_received(receiving_user, email):
    assert email in receiving_user.inbox

它比??yield fixture??要長一點,也更復(fù)雜一點,但當你在緊要關(guān)頭時,它確實提供了一些細微的差別。

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


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號