在我們運(yùn)行測(cè)試時(shí),我們希望確保它們?cè)谧约和瓿芍筮M(jìn)行清理,這樣它們就不會(huì)擾亂其他測(cè)試(也不會(huì)留下大量的測(cè)試數(shù)據(jù)來膨脹系統(tǒng))。pytest中的??fixture
??提供了一個(gè)非常有用的拆卸系統(tǒng),它允許我們?yōu)槊總€(gè)??fixture
??定義必要的特定步驟,以便在它們自己之后進(jìn)行清理。
該系統(tǒng)可以利用在兩個(gè)方面。
使用這些??fixture
??,我們可以運(yùn)行一些代碼并將一個(gè)對(duì)象傳回請(qǐng)求??fixture/test?
?,就像使用其他??fixture
??一樣。唯一的區(qū)別是:
return
??被換成了??yield
??。fixture
??的拆卸代碼位于生成之后。一旦pytest為??fixture
??確定了一個(gè)線性順序,它將運(yùn)行每個(gè)??fixture
??,直到它返回或產(chǎn)生,然后移動(dòng)到列表中的下一個(gè)??fixture
??來做同樣的事情。
測(cè)試完成后,pytest將返回??fixture
??列表,但順序相反,獲取每個(gè)產(chǎn)生的??fixture
??,并在其中運(yùn)行??yield
??語句之后的代碼。
作為一個(gè)簡(jiǎn)單的例子,考慮這個(gè)基本的電子郵件模塊:
# 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è)我們想測(cè)試從一個(gè)用戶向另一個(gè)用戶發(fā)送電子郵件。我們必須首先創(chuàng)建每個(gè)用戶,然后從一個(gè)用戶向另一個(gè)用戶發(fā)送電子郵件,最后斷言另一個(gè)用戶在他們的收件箱中收到了這條消息。如果我們想在測(cè)試運(yùn)行后進(jìn)行清理,我們必須確保在刪除其他用戶之前清空該用戶的郵箱,否則系統(tǒng)可能會(huì)報(bào)錯(cuò)。
這可能是這樣的:
# 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
因?yàn)??receiving_user
??是安裝期間運(yùn)行的最后一個(gè)??fixture
??,所以它是拆卸期間運(yùn)行的第一個(gè)??fixture
??。
$ pytest -q test_emaillib.py
. [100%]
1 passed in 0.12s
如果??yield fixture?
?在??yield
??之前引發(fā)異常,pytest將不會(huì)嘗試在該??yield fixture?
?的??yield
??語句之后運(yùn)行拆卸代碼。但是,對(duì)于已經(jīng)為該測(cè)試成功運(yùn)行的每個(gè)??fixture
??, pytest仍然會(huì)像正常情況一樣試圖將它們刪除。
雖然??yield fixture?
?被認(rèn)為是更干凈和更直接的選項(xiàng),但還有另一種選擇,即直接向測(cè)試的請(qǐng)求上下文對(duì)象添加??finalizer
??函數(shù)。它帶來了與??yield fixture?
?類似的結(jié)果,但需要更多的細(xì)節(jié)。為了使用這種方法,我們必須在需要添加??teardown
??代碼的??fixture
??中請(qǐng)求請(qǐng)求上下文對(duì)象(就像我們請(qǐng)求另一個(gè)??fixture
??一樣),然后將包含該??teardown
??代碼的可調(diào)用對(duì)象傳遞給它的??addfinalizer
??方法。但是,我們必須小心,因?yàn)閜ytest將在添加??finalizer
??后運(yùn)行該??finalizer
??,即使該??fixture
??在添加??finalizer
??后引發(fā)異常。因此,為了確保我們不會(huì)在不需要的時(shí)候運(yùn)行??finalizer
??代碼,我們只會(huì)在??fixture
??做了一些我們需要拆除的事情時(shí)添加??finalizer
??。下面是使用??addfinalizer
??方法的前一個(gè)例子:
# 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
??要長(zhǎng)一點(diǎn),也更復(fù)雜一點(diǎn),但當(dāng)你在緊要關(guān)頭時(shí),它確實(shí)提供了一些細(xì)微的差別。
$ pytest -q test_emaillib.py
. [100%]
1 passed in 0.12s
更多建議: