简体   繁体   中英

Run a test with two different pytest fixtures

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:

  1. Get the Selenium WebDriver instances

    @pytest.fixture() def browser(request, data, headless): b = webdriver.Firefox(executable_path=DRIVERS_PATH + '/geckodriver') yield b b.quit()
  2. 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
  3. 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?

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

So here is a example I worked out to demonstrate the reuse of fixtures. A fixture can reference another fixture - allowing for a layered approach to writing tests. Please see which one fits the bill for you:

import pytest

@pytest.yield_fixture()
def browser():
    print("Launching browser")
    b = {}
    yield b
    print("quitting browser")


@pytest.fixture()
def login(browser):
    print("logging in")


@pytest.fixture()
def page(login):
    print("on page")


@pytest.fixture()
def subpage(page):
    print("on subpage")


@pytest.yield_fixture()
def logout(page):
    yield page
    print('performing logout using fixtures')


def test_can_log_out(page):
    print("logging out using test")
    pass


def test_can_log_style2(logout):
    print("logging out using fixture")
    pass


def test_logout_page2(subpage, logout):
    print("test can logout from page 2")
    pass


def test_logout_page2_style2(subpage):
    print("test can logout from page 2 style2")
    test_can_log_out(subpage)
    pass

Output of test_can_log_out

Launching browser
logging in
on page
.logging out using test
quitting browser

Output of test_can_log_style2

Launching browser
logging in
on page
.logging out using fixture
performing logout using fixtures
quitting browser

Output of test_logout_page2

Launching browser
logging in
on page
on subpage
.test can logout from page 2
performing logout using fixtures
quitting browser

Output of test_logout_page2_style2

Launching browser
logging in
on page
on subpage
.test can logout from page 2 style2
logging out using test
quitting browser

Generally :

@pytest.fixture(scope="function")
def fixture_one():
    # set up things
    yield
    # teardown


@pytest.fixture(scope="function")
def fixture_two():
    # do things


@pytest.mark.parametrize('fixture_func', [fixture_one, fixture_two])
def test_things(fixture_func, request):
    request.getfixturevalue(fixture_func.__name__)
    assert foo == bar

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM