在我們運行測試時,我們希望確保它們在自己完成之后進行清理,這樣它們就不會擾亂其他測試(也不會留下大量的測試數(shù)據(jù)來膨脹系統(tǒng))。pytest中的??fixture
??提供了一個非常有用的拆卸系統(tǒng),它允許我們?yōu)槊總€??fixture
??定義必要的特定步驟,以便在它們自己之后進行清理。
該系統(tǒng)可以利用在兩個方面。
使用這些??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
??之前引發(fā)異常,pytest將不會嘗試在該??yield fixture?
?的??yield
??語句之后運行拆卸代碼。但是,對于已經(jīng)為該測試成功運行的每個??fixture
??, pytest仍然會像正常情況一樣試圖將它們刪除。
雖然??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
更多建議: