Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pytest fixture for a class through self not as method argument

Often I'll write a test class that uses a pytest fixture in every method. Here's an example. I'd like to be able to avoid having to write the fixture name in the signature of every method. It's not DRY. How can this be done?

I would like to be able to access the fixture by giving the fixture as an attribute of the test class. In this example, I would like to see the google fixture as an attribute of TestGoogle. Is this possible?

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" 
like image 316
Donal Avatar asked May 02 '18 10:05

Donal


People also ask

Can a class be a pytest fixture?

Like normal functions, fixtures also have scope and lifetime. The default scope of a pytest fixture is the function scope. Apart from the function scope, the other pytest fixture scopes are – module, class, and session.

Can pytest fixtures have arguments?

You can pass arguments to fixtures with the params keyword argument in the fixture decorator, and you can also pass arguments to tests with the @pytest. mark.

What is the correct syntax to apply a pytest fixture?

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}!"

What is pytest fixture Autouse true?

Fixtures with autouse=True are executed before other fixtures within the same scope.


1 Answers

Sure, just use an autouse fixture. Here is the relevant spot in pytest docs. In your example, the change would be introducing an extra fixture (I named it _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" 

You could even drop the google fixture completely and move the code to _request_google_page:

@pytest.fixture(autouse=True) def _request_google_page(self):     self._response = requests.get("https://www.google.com") 

Note that _request_google_page will be called once per test by default, so each test will get a new response. If you want the response to be initialized once and reused throughout all tests in the TestGoogle class, adjust the fixture scopes (scope='class' for _request_google_page and scope='module' or scope='session' for google). Example:

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" 
like image 121
hoefling Avatar answered Sep 18 '22 13:09

hoefling