问题
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