通常我会编写一个在每个方法中使用pytest夹具的测试类。下面是一个示例。我希望能够避免在每个方法的签名中写下夹具名称。它不是干燥的。如何做到这一点?
我希望能够通过将夹具作为测试类的属性来访问夹具。在这个例子中,我希望将谷歌夹具视为TestGoogle的一个属性。这可能吗?
from bs4 import BeautifulSoup
import pytest
import requests
@pytest.fixture()
def google():
return requests.get("https://www.google.com")
class TestGoogle:
def test_alive(self, google):
assert google.status_code == 200
def test_html_title(self, google):
soup = BeautifulSoup(google.content, "html.parser")
assert soup.title.text.upper() == "GOOGLE"
当然,只需使用自动使用灯具即可。这是pytest
文档中的相关位置。在您的示例中,更改将引入一个额外的夹具(我将其命名为_request_google_page
(:
from bs4 import BeautifulSoup
import pytest
import requests
@pytest.fixture()
def google():
return requests.get("https://www.google.com")
class TestGoogle:
@pytest.fixture(autouse=True)
def _request_google_page(self, google):
self._response = google
def test_alive(self):
assert self._response.status_code == 200
def test_html_title(self):
soup = BeautifulSoup(self._response.content, "html.parser")
assert soup.title.text.upper() == "GOOGLE"
您甚至可以完全放下google
装置并将代码移动到_request_google_page
:
@pytest.fixture(autouse=True)
def _request_google_page(self):
self._response = requests.get("https://www.google.com")
请注意,默认情况下,每个测试将调用一次_request_google_page
,因此每个测试都将获得新的响应。如果您希望响应初始化一次并在TestGoogle
类中的所有测试中重复使用,请调整夹具范围(scope='class'
用于_request_google_page
和scope='module'
或scope='session'
用于google
(。例:
from bs4 import BeautifulSoup
import pytest
import requests
@pytest.fixture(scope='module')
def google():
return requests.get("https://www.google.com")
@pytest.fixture(autouse=True, scope='class')
def _request_google_page(request, google):
request.cls._response = google
class TestGoogle:
def test_alive(self):
assert self._response.status_code == 200
def test_html_title(self):
soup = BeautifulSoup(self._response.content, "html.parser")
assert soup.title.text.upper() == "GOOGLE"
我必须解决类似的问题,而接受的解决方案对类范围的装置不起作用。
我想为每个测试类调用一次夹具,并使用self
在测试方法中重用该值。这实际上也是OP打算做的。
您可以使用request
装置访问使用它的类(request.cls
(,并在类属性中分配夹具值。然后,您可以从self
访问此属性。以下是完整的代码片段:
from bs4 import BeautifulSoup
import pytest
import requests
@pytest.fixture(scope="class")
def google(request):
request.cls.google = requests.get("https://www.google.com")
@pytest.mark.usefixtures("google")
class TestGoogle:
def test_alive(self):
assert self.google.status_code == 200
def test_html_title(self):
soup = BeautifulSoup(self.google.content, "html.parser")
assert soup.title.text.upper() == "GOOGLE"
希望这对其他提出这个问题的人有所帮助。
使用conftest.py
、夹具和普通类的组合,将所有测试(或模块化测试(共享相同的对象实例
我想分享我的解决方案,它来自 Pytest 文档(目前为 7.2(
模块化:使用夹具功能中的夹具
现在,如果我们将其与conftest.py
相结合,这就是我们可以做的: 首先具有此结构(仅显示子模块,并且所有测试都实例化一次对象(
├── conftest.py
├── sub_folder
│ ├── __init__.py
│ └── test_sub_1.py
├── test_1.py
├── test_2.py
这是内容conftest.py
"""tests/conftest.py"""
import pytest
class MockServer():
def get(self, url):
return "hello-world"
class App:
def __init__(self, http_connection):
print("APP CREATED")
self.http_connection = http_connection
@pytest.fixture(scope="session")
def http_connection():
print("HTTP_CONNECTION FIXTURE")
return MockServer()
@pytest.fixture(scope="session")
def app(http_connection):
print("CREATE APP")
return App(http_connection)
测试/test_1.py
class TestClass1:
def test_1(self, app):
http_connection = app.http_connection
assert http_connection.get("my-url") == "hello-world"
def test_2(self, app):
http_connection = app.http_connection
assert http_connection.get("my-url") == "hello-world"
测试/test_2.py
class TestClass2:
def test_1(self, app):
http_connection = app.http_connection
assert http_connection.get("my-url") == "hello-world"
def test_2(self, app):
http_connection = app.http_connection
assert http_connection.get("my-url") == "hello-world"
测试/sub_folder/test_sub_1.py
"""tests/sub_folder/test_sub_1.py"""
class TestSubClass1:
def test_sub_1(self, app):
http_connection = app.http_connection
assert http_connection.get("my-url") == "hello-world"
def test_sub_2(self, app):
http_connection = app.http_connection
assert http_connection.get("my-url") == "hello-world"
现在,让我们运行
pytest -rP
输出应该是这样的
tests/test_1.py::TestClass1::test_1 PASSED [ 16%]
tests/test_1.py::TestClass1::test_2 PASSED [ 33%]
tests/test_2.py::TestClass2::test_1 PASSED [ 50%]
tests/test_2.py::TestClass2::test_2 PASSED [ 66%]
tests/sub_folder/test_sub_1.py::TestSubClass1::test_sub_1 PASSED [ 83%]
tests/sub_folder/test_sub_1.py::TestSubClass1::test_sub_2 PASSED [100%]
==================================================================== PASSES ====================================================================
______________________________________________________________ TestClass1.test_1 _______________________________________________________________
------------------------------------------------------------ Captured stdout setup -------------------------------------------------------------
HTTP_CONNECTION FIXTURE
CREATE APP
APP CREATED
============================================================== 6 passed in 0.23s ===============================================================
如您所见,通过输出,(HTTP_CONNECTION FIXTURE, CREATE APP, APP CREATED(仅运行一次。当我们需要在所有测试之间共享资源时,这很好。 话虽如此,现在让我们将其与唄头答案结合起来,我只添加测试/test_1.py
测试/test_1.py
"""tests/test_1.py"""
import pytest
class TestClass1:
@pytest.fixture(autouse=True)
def _app(self, app):
self.app = app
def test_1(self):
assert self.app.http_connection.get("my-url") == "hello-world"
def test_2(self):
assert self.app.http_connection.get("my-url") == "hello-world"
这已经更好了,但我们可以更进一步,让我们有一个 Base 测试类,并让我们的测试继承自它conftest.py
"""tests/conftest.py"""
import pytest
class MockServer():
def get(self, url):
return "hello-world"
class App:
def __init__(self, http_connection):
print("APP CREATED")
self.http_connection = http_connection
@pytest.fixture(scope="session")
def http_connection():
print("HTTP_CONNECTION FIXTURE")
return MockServer()
@pytest.fixture(scope="session")
def app(http_connection):
print("CREATE APP")
return App(http_connection)
class Base:
@pytest.fixture(autouse=True)
def _app(self, app):
self.app = app
现在""测试/test_1.py""可以看起来像
"""tests/test_1.py"""
from conftest import Base
class TestClass1(Base):
def test_1(self):
assert self.app.http_connection.get("my-url") == "hello-world"
def test_2(self):
assert self.app.http_connection.get("my-url") == "hello-world"