How do i make the pytest driver instance available in my testcase

核能气质少年 提交于 2020-01-15 01:46:10

问题


I am trying to build an selenium based automation framework, using, Python, Pytest. My intention is to create a driver instance at the class level by initializing it in conftest.py and making it available in all testcases, so that user doesn't need to create the driver instance in each testcase.

Driver instance in conftest.py:

@pytest.fixture(scope="class")
def get_driver(request):
    from selenium import webdriver
    driver = webdriver.Chrome()
    request.cls.driver = driver
    yield
    driver.quit()

BaseTestCase class looks like this:

@pytest.mark.usefixtures("get_driver")
class BaseTestCase(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(BaseTestCase, self).__init__(*args, **kwargs)

    @classmethod
    def setUpClass(cls):
        if hasattr(super(BaseTestCase, cls), "setUpClass"):
            super(BaseTestCase, cls).setUpClass()

My testcase is as follows:

from ..pages.google import Google

class Test_Google_Page(BaseTestCase):

    @classmethod
    def setUpClass(self):
        self.page = Google(self.driver, "https://www.google.com/")

My page Google extends to BasePage that looks like this:

class BasePage(object):
    def __init__(self, driver, url=None, root_element = 'body'):
        super(BasePage, self).__init__()

        self.driver = driver
        self._root_element = root_element
        self.driver.set_script_timeout(script_timeout)

When i execute my testcase, I get the following error:

    @classmethod
    def setUpClass(self):
>       driver = self.driver
E       AttributeError: type object 'Test_Google_Page' has no attribute 'driver'

How can I make the driver instance available in my testcases by simply calling self.driver?


回答1:


The class scoped fixtures are executed after the setUpClass classmethods, so when Test_Google_Page.setUpClass is executed, get_driver did not run yet. Check out the execution ordering:

import unittest
import pytest


@pytest.fixture(scope='class')
def fixture_class_scoped(request):
    print(f'{request.cls.__name__}::fixture_class_scoped()')


@pytest.mark.usefixtures('fixture_class_scoped')
class TestCls(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print(f'{cls.__name__}::setUpClass()')

    def setUp(self):
        print(f'{self.__class__.__name__}::setUp()')

    @pytest.fixture()
    def fixture_instance_scoped(self):
        print(f'{self.__class__.__name__}::fixture_instance_scoped()')

    @pytest.mark.usefixtures('fixture_instance_scoped')
    def test_bar(self):
        print(f'{self.__class__.__name__}::test_bar()')
        assert True

When running the test with e.g. pytest -sv, the output yields:

TestCls::setUpClass()
TestCls::fixture_class_scoped()
TestCls::fixture_instance_scoped()
TestCls::setUp()
TestCls::test_bar()

So the solution is to move the code from setUpClass to e.g. setUp:

class Test_Google_Page(BaseTestCase):

    def setUp(self):
        self.page = Google(self.driver, "https://www.google.com/")

I am afraid I may not use setUp because in my most of my class file I have multiple test cases and I just want to do one time setup in setUpClass instead of launching in each time before calling any test method.

I would then move the code from setUpClass to another class-scoped fixture:

import pytest

@pytest.mark.usefixtures('get_driver')
@pytest.fixture(scope='class')
def init_google_page(request):
    request.cls.page = Google(request.cls.driver, 
                              "https://www.google.com/")


@pytest.mark.usefixtures('init_google_page')
class Test_Google_Page(BaseTestCase):
    ...

The former setUpClass is now the init_google_page fixture which will be called after get_driver (because of pytest.mark.usefixtures('get_driver')).



来源:https://stackoverflow.com/questions/51682413/how-do-i-make-the-pytest-driver-instance-available-in-my-testcase

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!