Use Selenium Driver When Created Outside Object - python-3.x

I'm having an issue where when I pass a Selenium driver object and assign it as an attribute of said class, that nothing happens when I call it from inside that class. An abstraction of what I'm trying to do is this, though I created this also and verified that this also doesn't work:
def get_driver():
chrome_path = r'C:\path\to\chromedriver\chromedriver.exe'
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(chrome_options=options, executable_path=chrome_path)
return driver
class Page:
def __init__(self, driver):
self.driver = driver
def get_page(self, url):
print(1)
self.driver.get(url)
print(2)
driver = get_driver()
p = Page(driver)
p.get_page('http://somepage.com')
The output of the above is:
1
2
But selenium does not get the page. But when I run it outside the class like this:
driver = get_driver()
driver.get('http://somepage.com')
Selenium does retrieve the page.
Why is the driver inside the class unresponsive when I call it from a class to which I have assigned it?
I'm running this on Win 10 with Python 3.7 and Chrome version 79.x.

Related

Send arguments to Pytest fixture for Selenium

Update1:
Thank you for your answer.
It almost solves the problem. If I could be more precise:
How to instantiate the two drivers in 'init_pages', instead of creating two same methods for each driver?
The new snippet as you suggested:
#pytest.mark.usefixtures("driver_init")
class BaseTestFF:
#pytest.fixture
def init_data(self, driver_init):
self.web_driver = driver_init('firefox') #how to inject data via external commands? jenkins?
self.web_driver2 = driver_init('chrome')
#pytest.fixture(autouse=True)
#pytest.mark.usefixtures('init_data')
def init_pages(self, init_data):
#how to instanciate the two drivers here? istead of creating two same methods for each driver
self.login_page = LoginPage(self.web_driver)
self.application_page = ApplicationPage(self.web_driver)
In my selenium project, I would like to be able to send arguments, instead of them to be fixed in the conftest file.
For each different method, I would like it to use different browsers for different things.
Is there any way to send arguments TO the fixture??
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver import DesiredCapabilities
#pytest.fixture(scope="class", params=['chrome']) #i would like to send here dynamic fixtures.
def driver_init(request):
if request.param == 'firefox':
web_driver = webdriver.Firefox()
elif request.param == 'edge':
web_driver = webdriver.Edge() # dk:needs to be added the path
else:
options = Options()
options.add_argument(
r'user-data-dir=Users/dannyk/Library/Application Support/Google/Chrome/Default')
web_driver = webdriver.Chrome(chrome_options=options)
request.cls.driver = web_driver
yield
web_driver.close()
#pytest.mark.usefixtures("driver_init")
class BaseTest:
#pytest.fixture(autouse=True)
def init_pages(self):
self.login_page = lambda driver_type=self.driver: LoginPage(driver_type)
self.application_page = lambda driver_type=self.driver: ApplicationPage(driver_type)
class TestT4(BaseTest):
# #pytest.fixture(autouse=True)
#pytest.mark.run(order=1) #here i would like to use firefox for the driver
def test_init_firefox(self):
self.getting_start_page.go_home_page()
#pytest.mark.run(order=2) #here i would like to use chrome for the driver
def test_init_chrome(self):
self.getting_start_page.go_home_page()
You can use the "factory as fixture" pattern in conftest, which is basically an inner function inside the fixture that accepts parameters. You can then pass them from your test as arguments.
In conftest:
#pytest.fixture
def driver_init():
web_drivers = []
def _driver_init(browser):
if browser == 'firefox':
web_driver = webdriver.Firefox()
web_drivers.append(web_driver)
elif browser == 'edge':
web_driver = webdriver.Edge() # dk:needs to be added the path
web_drivers.append(web_driver)
else:
options = Options()
options.add_argument(
r'user-data-dir=Users/dannyk/Library/Application Support/Google/Chrome/Default')
web_driver = webdriver.Chrome(chrome_options=options)
web_drivers.append(web_driver)
return web_driver
yield _driver_init
for web_driver in web_drivers:
web_driver.quit()
Then, in your test:
class TestT4(BaseTest):
#pytest.mark.run(order=1) #here i would like to use firefox for the driver
def test_init_firefox(self, driver_init):
web_driver = driver_init("firefox")
#pytest.mark.run(order=2) #here i would like to use chrome for the driver
def test_init_chrome(self, driver_init):
web_driver = driver_init("chrome")
Or you can use the same concept on your def_init_pages(self): fixture to instantiate the pages and send the arguments from your tests.
Please check the official documentation here.
Update
For your first new question:
how to inject data via external commands? jenkins?
I believe what you're looking for is the def pytest_addoption(parser) fixture. It allows you to pass arguments to your test automation through the command line, when running Pytest. So you could pass the browser you want your tests to run with, locally or with Jenkins, with something like this:
In conftest:
def pytest_addoption(parser):
parser.addoption("--browser")
#pytest.fixture
def browser(request):
return request.config.getoption("--browser")
Then, on your driver_init fixture you wouldn't use the "factory as fixture" pattern:
#pytest.fixture
def driver_init(browser):
if browser == 'firefox':
web_driver = webdriver.Firefox()
elif browser == 'edge':
web_driver = webdriver.Edge() # dk:needs to be added the path
else:
options = Options()
options.add_argument(
r'user-data-dir=Users/dannyk/Library/Application Support/Google/Chrome/Default')
web_driver = webdriver.Chrome(chrome_options=options)
yield web_driver
web_driver.quit()
Then, to run your tests, you would pass the browser as argument:
pytest --browser=firefox
Please check these documents:
Pass different values to a test function, depending on command line options
pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) → None
For your second new question:
how to instanciate the two drivers here? istead of creating two same methods for each driver
I think what might be the answer is the fixture parametrization that you used on your original question, but then with the driver_init fixture I suggested earlier (you wouldn't need the def init_data(self, driver_init): fixture you wrote):
#pytest.fixture(autouse=True, params=["firefox", "chrome", "edge"])
def init_pages(self, request, driver_init):
self.login_page = LoginPage(driver_init(request.param))
self.application_page = ApplicationPage(driver_init(request.param))
Please note that I have removed the #pytest.mark.usefixtures as it has no effect when applied to another fixture. You need to declare what fixture you're using as a parameter:
#pytest.fixture(autouse=True)
def init_pages(self, init_data):
Please check the pytest.mark.usefixtures documentation
I hope this gives you more tools to figure out what you need to do to sort your problem. I'm not sure if I understand completely what you're trying to accomplish.

PyCharm type hinting does not work when I pass selenium driver as an argument

In one class I initialize a browser using selenium.webdriver and I want to use the same already opened browser in a different class. So I want to pass this webdriver as an argument to the function in that other class.
However, no matter what I try, PyCharm is not giving me any hint about available methods of this webdriver object, even if I give it a hint that it is indeed a webdriver from selenium.
How can I solve this properly? I know that I can initialize a webdriver and after end of work on that class i could remove it but this is not the right solution I guess.
My code:
from emails import FormalEmailInterface
from selenium import webdriver
class EmailTest(FormalEmailInterface):
def __init__(self, driver: webdriver, url: str):
self.driver = driver
self.url = url
""""Get temporary email address"""
def get_temp_email(self):
# self.driver. <-- NO HINTS HERE!!!
print(type(self.driver))
pass
Screen 1 - no hinting when webdriver comes from argument:
Screen 2 - properly hinting when webdriver comes straight from import:
My solution is import specific webdriver like Chrome object and set type in drive self.driver: Chrome, now we have webelement method
from emails import FormalEmailInterface
from selenium import webdriver
from selenium.webdriver import Chrome
class EmailTest(FormalEmailInterface):
def __init__(self, driver: webdriver, url: str):
self.driver: Chrome = driver
self.url = url
""""Get temporary email address"""
def get_temp_email(self):
self.driver.
print(type(self.driver))
pass
Either of these options should do the trick:
from selenium.webdriver.remote.webdriver import WebDriver
class EmailTest(FormalEmailInterface):
def __init__(self, driver: WebDriver, url: str):
self.driver: WebDriver = driver
from typing import Union
from selenium import webdriver
typeWebDriver = Union[webdriver.Firefox,
webdriver.Chrome,
webdriver.Edge,
webdriver.Safari,
webdriver.PhantomJS
]
class EmailTest(FormalEmailInterface):
def __init__(self, driver: typeWebDriver, url: str):
self.driver: typeWebDriver = driver

How to run tearDown and setUp in conftest.py

I need help with my pytest/selenium code.
I have the following code in my conftest.py file.
import pytest
from base.webdriverfactory import WebDriverFactory
from pages.login_page import LoginPage
#pytest.fixture(scope="class")
def oneTimesetUp(request, browser):
print("Running one time setUp")
wdf = WebDriverFactory(browser)
driver = wdf.getWebDriverInstance()
lp = LoginPage(driver)
lp.login("haykpo", "Aaaa4321")
if request.cls is not None:
request.cls.driver = driver
yield driver
lp.log_out_from_wp_page()
driver.quit()
print("Running one time tearDown")
And here is my test file where I use this fixture
#pytest.mark.usefixtures("oneTimesetUp", "tearDown")
class TestPublishWithOfflinewAsset:
#pytest.fixture(autouse=True)
def objectSetup(self, oneTimesetUp):
self.pub = TestPublishofflineWOMedia(self.driver)
def test_publish_with_off_w_media(self):
self.pub.select_project('Test cases - Avallain')
self.pub.check_lo_plays_normally(15292)
I know that whatever comes after yield is meant to be code as tearDown. So I have written these 2 lines in order to log out from the app and quit the browser at the end of the test execution.
lp.log_out_from_wp_page()
driver.quit()
However, the problem is that the log out part (not the quit one interestingly) is always being executed after few steps when my test starts and when browser focus switch to iframe during test execution (or may be this is just a coincidence am not sure if iframe is guilty here). How can I compose my code to run my teardown log out or whatever I want after yield as intended but not during test execution?
For more details here is the detailed files with code.
The below one is my Login page
from base.basepage import BasePage
class LoginPage(BasePage):
def __init__(self, driver):
super().__init__(driver)
self.driver = driver
### Locators ###
_name_field = "form1:Name"
_password_field = "form1:Password"
_login_button = "form1:loginButton"
_work_packages = "wpButton"
_user_logout_dropdown_button = 'rolebutton_button'
_logout_button = 'blogout'
def log_out_from_wp_page(self):
self.elementClick(locator=self._user_logout_dropdown_button)
self.driver.implicitly_wait(5)
self.elementClick(locator=self._logout_button)
And below one is webdriver factory
class WebDriverFactory:
def __init__(self, browser):
self.browser = browser
def getWebDriverInstance(self):
"""
Get WebDriver Instance based on the browser configuration
Returns:
'WebDriver Instance'
"""
global driver
baseURL = "https://example.com"
if self.browser == "iexplorer":
# Set ie driver
driver = webdriver.Ie()
elif self.browser == "firefox":
driver = webdriver.Firefox()
elif self.browser == "chrome":
# Set chrome driver
driver = webdriver.Chrome()
#driver.set_window_size(1440, 900)
else:
driver = webdriver.Chrome()
# Setting Driver Implicit Time out for An Element
driver.implicitly_wait(10)
# Maximize the windows
driver.maximize_window()
# Loading browser with App URL
driver.get(baseURL)
return driver
The below one is BasePsge
class BasePage(SeleniumDriver):
def __init__(self, driver):
super(BasePage, self).__init__(driver)
self.driver = driver

AttributeError: 'ClassName' object has no attribute 'driver' on Appium Python

I am using this body(desired_caps are set properly in config file)
Whatever I do I receive 'AttributeError: 'ClassName' object has no attribute 'driver'' or similar errors - no find_element_by_xpath attribute or whatever.
Do you have any suggestions? I am doing in the same way as in lectures, maybe anything related to appium + python setups?
import unittest
from appium import webdriver
import time
import tracemalloc
tracemalloc.start()
from config import desired_caps
# self = webdriver
# self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
class BaseTest(unittest.TestCase):
def test_testcase1(self):
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
def test_credentials(self):
email = self.driver.find_element_by_xpath("proper Xpath")
email.send_keys("Test")
save = self.driver.find_element_by_link_text("Log In")
save.click()
def tearDown(self):
self.driver.quit()
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(BaseTest)
unittest.TextTestRunner(verbosity=3).run(suite)
you need to make your driver in a function named setUp(). The unit test suite executes kinda like this.
setUp()
run test_testcase1()
tearDown()
setUp()
run test_credentials()
teardown()
...etc...
if driver driver is not made in setup() the other tests will not know about it. Unless you make driver in every single test. Same goes for any other test variables you'd need.
This way each test is independent of each other, and each test gets a fresh start.

str object has no attribute 'get'

I am using unittest example in selenium python
tried google did not get correct solution
from selenium import webdriver
import unittest
#import HtmlTestRunner
class googlesearch(unittest.TestCase):
driver = 'driver'
#classmethod
def setupClass(self):
self.driver = webdriver.Chrome(chrome_options=options)
self.driver.implicitly_wait(10)
self.driver.maximize_window()
def test_search_automationstepbystep(self):
self.driver.get("https://google.com")
self.driver.find_element_by_name("q").send_keys("Automation Step By step")
self.driver.find_element_by_name("btnk").click()
def test_search_naresh(self):
self.driver.get("https://google.com")
self.driver.find_element_by_name("q").send_keys("Naresh")
self.driver.find_element_by_name("btnk").click()
#classmethod
def teardownClass(self):
self.driver.close()
self.driver.quit()
print("Test completed")
if __name__== "__main__":
unittest.main()
As mentioned #Error - Syntactical Remorse , driver is a string due to your first line of code in your class.
If you are planning to access the driver globally make sure to declare the driver as global.

Resources