Reputation: 89
I have example project in PyCharm - consist of a simple test that checking sign-in to the given correct Slack workspace. It has web_drivers
directory with chromedriver
inside, conftest.py
with webdriver setup for test and tests.py
with actual test, f.e.
conftest.py
import os
import pytest
from selenium import webdriver
@pytest.fixture(scope='class')
def driver_get(request):
web_driver = webdriver.Chrome(executable_path=os.path.join("web_drivers","chromedriver.exe"))
yield web_driver
def fin():
web_driver.close()
request.addfinalizer(fin)
tests.py
import pytest
class TestSlackWorkspace(object):
@pytest.fixture(autouse=True)
def setup(self, driver_get):
self.driver = driver_get
self.driver.get("https://slack.com/signin")
self.input_field = self.driver.find_element_by_xpath(
"//input[@type='text' and @id='domain']")
self.continue_button = self.driver.find_element_by_xpath(
"//button[@id='submit_team_domain']")
def test_correct_workspace(self):
self.input_field.send_keys("test")
self.continue_button.click()
assert self.driver.find_element_by_xpath("//h1[@id='signin_header']"
).is_displayed(), "Login page should be displayed"
Now the question is to divide test to the page initialization part - def setup
, and actual test execution part - def test_correct_workspace
to the different classes and files(something like Page Object Pattern)
So base of conftest.py
should be the same, and divide test.py
to i.e.
page.py
class SlackWorkspace(object):
@pytest.fixture(autouse=True)
def __init__(self, driver_get):
self.driver = driver_get
self.driver.get("https://slack.com/signin")
self.input_field = self.driver.find_element_by_xpath("//input[@type='text' and @id='domain']")
self.continue_button = self.driver.find_element_by_xpath("//button[@id='submit_team_domain']")
test.py
class TestWorkspace(object):
def test_correct_workspace(self):
self.input_field.send_keys("test")
self.continue_button.click()
login_page = self.driver.find_element_by_xpath("//h1[@id='signin_header']")
assert login_page.is_displayed(), "Login page should be displayed"
But for sure it will not work in that form:
1) Somehow driver_get
should be imported to page initialization file and forwarder to __init__
- ?
2) Somehow page initialization should be linked with test realization in other files-?
Have no idea how to organize all these imports between separate files
Upvotes: 2
Views: 1604
Reputation: 89
Managed to find solution for this with help of pytest -> How to use fixture return value in test method under a class and some explanation from more expirienced person in the following way
1) Turns out that fixture in conftest needs to pass initialized webdriver for my signin page from pages.py
and initialize this page class with this driver using built-in request
fixture. So correct conftest.py
will looks like:
import pytest
import os
from selenium import webdriver
from pages import SigninPage
@pytest.fixture(scope='class', autouse=True)
def driver_get(request):
request.cls.webdriver = webdriver.Firefox(executable_path=os.path.join("web_drivers", "geckodriver"))
request.cls.signin = SigninPage(request.cls.webdriver)
yield request.cls.webdriver
def fin():
request.cls.webdriver.quit()
request.addfinalizer(fin)
2) 'pages.py' include init of SigninPage
with received webdriver, input field to enter workspace and button to continue, method enter_workspace
that actually does it and return LogInPage
with login_page
field to be checked, so it looks like:
class SigninPage(object):
def __init__(self, web_driver):
self.driver = web_driver
self.driver.get("https://slack.com/signin")
self.input_field = self.driver.find_element_by_xpath("//input[@type='text' and @id='domain']")
self.continue_button = self.driver.find_element_by_xpath("//button[@id='submit_team_domain']")
def enter_workspace(self):
self.input_field.send_keys("test")
self.continue_button.click()
return LogInPage(self.driver)
class LogInPage(object):
def __init__(self, web_driver):
self.driver = web_driver
self.login_page = self.driver.find_element_by_xpath("//h1[@id='signin_header']")
3) And finnaly, test.py
consist of 2 things - entering workspace by calling method enter_workspace
from SigninPage
, which opens LogInPage
, and then checking if login actually displayed:
class TestSlackWorkspace(object):
def test_workspace(self):
login = self.signin.enter_workspace()
assert login.login_page.is_displayed(), "Missing!"
It still need to be smproved, but asked problem is solved. Thank you.
Upvotes: 0
Reputation: 20456
In Page Object pattern, direct reference to driver should be avoided in the test classes. You can have page.py as a base class to have common methods. The set up can be moved to a different page e.g. login.py. This set up method should return the page you are trying to verify. Test methods should then use these page objects for verification. I keep login as a fixture in conftest.py then use it across tests & other fixtures. For example, here's my attempt to give you an overview. You should read more on page object pattern.
I recommend using pytest-selenium plugin which reduces a lot of boilerplate code with using selenium with pytest
conftest.py
@fixture
def selenium():
# pytest-selenium provides this fixture & you can override it if required.
return selenium
@fixture
def home(selenium):
#do login
return HomePage
login.py
from page import Page #Import base class with common methods.
class LoginPage(Page):
def login(self, driver):
#do the login steps
return HomePage #return Landing Page
test_login.py
def test_login_successful(home): #Use the home fixture
assert home.is_displayed("xyz")
Upvotes: 2