pytest的??fixture
??系統(tǒng)非常強(qiáng)大,但它仍然是由計(jì)算機(jī)運(yùn)行的,所以它無法知道如何安全地拆除我們?nèi)咏o它的所有東西。如果我們不小心,錯(cuò)誤位置的錯(cuò)誤可能會留下測試遺留的內(nèi)容,這可能會很快導(dǎo)致進(jìn)一步的問題。
例如,考慮以下測試(基于上面的郵件示例):
# content of test_emaillib.py
import pytest
from emaillib import Email, MailAdminClient
@pytest.fixture
def setup():
mail_admin = MailAdminClient()
sending_user = mail_admin.create_user()
receiving_user = mail_admin.create_user()
email = Email(subject="Hey!", body="How's it going?")
sending_user.send_email(email, receiving_user)
yield receiving_user, email
receiving_user.clear_mailbox()
mail_admin.delete_user(sending_user)
mail_admin.delete_user(receiving_user)
def test_email_received(setup):
receiving_user, email = setup
assert email in receiving_user.inbox
這個(gè)版本更緊湊,但也更難閱讀,沒有一個(gè)非常描述性的??fixture
??名稱,而且沒有一個(gè)??fixture
??可以很容易地重用。
還有一個(gè)更嚴(yán)重的問題,即如果設(shè)置中的任何一個(gè)步驟引發(fā)異常,則所有的銷毀代碼都不會運(yùn)行。
一種選擇可能是使用??addfinalizer
??方法,而不是??yield fixture?
?,但這可能會變得非常復(fù)雜和難以維護(hù)(而且它將不再緊湊)。
$ pytest -q test_emaillib.py
. [100%]
1 passed in 0.12s
最安全、最簡單的??fixture
??結(jié)構(gòu)要求限制每個(gè)??fixture
??只做一個(gè)狀態(tài)更改操作,然后將它們與拆卸代碼捆綁在一起,如上面的電子郵件示例所示。
狀態(tài)更改操作失敗但仍然修改狀態(tài)的幾率可以忽略不計(jì),因?yàn)檫@些操作大多是基于事務(wù)的(至少在可能留下狀態(tài)的測試級別上)。因此,如果我們通過將任何成功的狀態(tài)更改操作移動到一個(gè)獨(dú)立的??fixture
??函數(shù),并將其與其他可能失敗的狀態(tài)更改操作分開,從而確保任何成功的狀態(tài)更改操作都被刪除,那么我們的測試將最有可能以發(fā)現(xiàn)它的方式離開測試環(huán)境。
例如,假設(shè)我們有一個(gè)帶有登錄頁面的網(wǎng)站,我們可以訪問一個(gè)管理API,在那里我們可以生成用戶。對于我們的測試,我們想:
我們不想讓這個(gè)用戶留在系統(tǒng)中,也不想讓瀏覽器會話繼續(xù)運(yùn)行,所以我們希望確保創(chuàng)建這些東西的?fixture
?在它們自己清理之后。
這可能是這樣的:
from uuid import uuid4
from urllib.parse import urljoin
from selenium.webdriver import Chrome
import pytest
from src.utils.pages import LoginPage, LandingPage
from src.utils import AdminApiClient
from src.utils.data_types import User
@pytest.fixture
def admin_client(base_url, admin_credentials):
return AdminApiClient(base_url, **admin_credentials)
@pytest.fixture
def user(admin_client):
_user = User(name="Susan", username=f"testuser-{uuid4()}", password="P4$word")
admin_client.create_user(_user)
yield _user
admin_client.delete_user(_user)
@pytest.fixture
def driver():
_driver = Chrome()
yield _driver
_driver.quit()
@pytest.fixture
def login(driver, base_url, user):
driver.get(urljoin(base_url, "/login"))
page = LoginPage(driver)
page.login(user)
@pytest.fixture
def landing_page(driver, login):
return LandingPage(driver)
def test_name_on_landing_page_after_login(landing_page, user):
assert landing_page.header == f"Welcome, {user.name}!"
依賴項(xiàng)的布局方式意味著不清楚用戶??fixture
??是否會在驅(qū)動程序??fixture
??之前執(zhí)行。但這沒關(guān)系,因?yàn)檫@些都是原子操作,所以先運(yùn)行哪個(gè)并不重要因?yàn)闇y試的事件序列仍然是線性的。但真正重要的是,無論哪一個(gè)先運(yùn)行,如果其中一個(gè)引發(fā)異常而另一個(gè)沒有,那么兩者都不會留下任何東西。如果驅(qū)動程序在用戶之前執(zhí)行,并且用戶引發(fā)了異常,驅(qū)動程序仍然會退出,并且用戶從未被創(chuàng)建。如果驅(qū)動程序是引發(fā)異常的那個(gè),那么驅(qū)動程序?qū)⒂肋h(yuǎn)不會被啟動,用戶也永遠(yuǎn)不會被創(chuàng)建。
更多建議: