I am currently testing a web app using pytest and Selenium. All pages have "Home" and "Log Out" links, so I have written a test like this:
def test_can_log_out(page):
link = page.find_element_by_partial_link_text('Log Out')
link.click()
assert 'YOU HAVE SUCCESSFULLY LOGGED OFF!' in starting_page.page_source
Now for the page
fixture, I am simulating the login process. I have this broken into several fixtures:
Get the Selenium WebDriver instances
@pytest.fixture()
def browser(request, data, headless):
b = webdriver.Firefox(executable_path=DRIVERS_PATH + '/geckodriver')
yield b
b.quit()
Log in to the web app
@pytest.fixture()
def login(browser):
browser.get('http://example.com/login)
user_name = browser.find_element_by_name('user_name')
user_name.send_keys('codeapprentice')
password = browser.find_element_by_name('password')
password.send_keys('password1234')
submit = browser.find_element_by_name('submit')
submit.click()
return browser
Visit a page
@pytest.fixture()
def page(login):
link = login.find_element_by_partial_link_text('Sub Page A')
link.click()
return login
This works very well and I can test logging out from this page. Now my question is that I have another page which can be visited from "Page A":
@pytest.fixture()
def subpage(page):
button = login.find_element_name('button')
button.click()
return page
Now I want to run the exact same test with this fixture, also. Of course, I can copy/paste and make a few changes:
def test_can_log_out_subpage(subpage):
link = page.find_element_by_partial_link_text('Log Out')
link.click()
assert 'YOU HAVE SUCCESSFULLY LOGGED OFF!' in starting_page.page_source
However, this violates the DRY principle. How can I reuse test_can_log_out()
without this repetition?
A fixture can use multiple other fixtures. Just like a test method can take multiple fixtures as arguments, a fixture can take multiple other fixtures as arguments and use them to create the fixture value that it returns.
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.
By default, pytest runs tests in sequential order. In a real scenario, a test suite will have a number of test files and each file will have a bunch of tests. This will lead to a large execution time. To overcome this, pytest provides us with an option to run tests in parallel.
Those are the things that need to test a certain action. In pytest the fixtures are functions that we define to serve these purpose, we can pass these fixtures to our test functions (test cases) so that they can run and set up the desired state for you to perform the test.
Here, you can pass your fixtures
which gives your pages and subpages in test parameters which would be called dynamically as a first step of test. Like below.
When fixtures are on same page where tests are:
testfile.py
import pytest
class TestABC():
@pytest.fixture
def browser(self,request):
print "browser"
@pytest.fixture
def login(self,request,browser):
print "login"
@pytest.fixture
def subpage1(self,request,login):
print "subpage1"
@pytest.fixture
def subpage2(self, request, login):
print "subpage2"
@pytest.fixture
def subpage3(self, request, login):
print "subpage3"
@pytest.mark.parametrize('sub_page',
['subpage1', 'subpage2', 'subpage3'])
def test_can_log_out_subpage(self,sub_page,request):
request.getfixturevalue(sub_page) # with pytest>=3.0.0 use getfixturevalue instead of getfuncargvalue
print "test output of ", sub_page
Output:
browser
login
subpage1
test output of subpage1
browser
login
subpage2
test output of subpage2
browser
login
subpage3
test output of subpage3
When fixtures are at conftest.py
import pytest
@pytest.fixture
def browser(request):
print "browser"
@pytest.fixture
def login(request):
print "login"
@pytest.fixture
def subpage1(request,login):
print "subpage1"
@pytest.fixture
def subpage2(request, login):
print "subpage2"
@pytest.fixture
def subpage3(request, login):
print "subpage3"
testfile.py
import pytest
class TestABC():
@pytest.mark.parametrize('sub_page',
['subpage1', 'subpage2', 'subpage3'])
def test_can_log_out_subpage(self,sub_page,request):
request.getfixturevalue(sub_page) # with pytest>=3.0.0 use getfixturevalue instead of getfuncargvalue
print "test output of ", sub_page
Here, you will also get same output as above.
Hope it would help you.
Another solution that worked for me was to use classes. It requires a bit more setup than the solutions above, but it can be helpful if you end up having to setup multiple related tests.
You define all of your tests in a single class, and then create a dummy class for each fixture extending that first class:
@pytest.fixture(scope='class')
def fixture1(request):
request.cls.fruit = 'apple'
@pytest.fixture(scope='class')
def fixture2(request):
request.cls.fruit = 'banana'
class ClassOfRelatedTests:
def test_is_banana(self):
assert self.fruit == 'banana'
def test_not_spinach(self):
assert self.fruit != 'spinach'
@pytest.mark.usefixtures("fixture1")
class TestFixture1(ClassOfRelatedTests):
pass
@pytest.mark.usefixtures("fixture2")
class TestFixture2(ClassOfRelatedTests):
pass
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With