Pytest, selenium, fixture throws an error after the first test InvalidSessionIdException - python

I have a fixture file.
import pytest
from selenium.webdriver.chrome.options import Options as chrome_options
from driver.singleton_driver import WebDriver
#pytest.fixture
def get_chrome_options():
options = chrome_options()
options.add_argument('chrome')
options.add_argument('--start-maximized')
options.add_argument('--window-size=1920,1080')
options.add_argument('--incognito')
return options
#pytest.fixture
def get_webdriver(get_chrome_options):
options = get_chrome_options
driver = WebDriver(options).driver
return driver
#pytest.fixture(scope='function')
def setup(request, get_webdriver):
driver = get_webdriver
if request.cls is not None:
request.cls.driver = driver
yield driver
driver.close()
File with my tests
import pytest
#pytest.mark.usefixtures('setup')
class TestSteamPages:
def test_first(self):
self.driver.get('https://www.google.com/')
def test_second(self):
self.driver.get('https://www.google.com/')
As I understand it, after the first test, the function in the driver.close() fixture is triggered. But when the second test is run, the fixture does not restart the webdriver. An error
venv/lib/python3.10/site-packages/selenium/webdriver/remote/errorhandler.py:243: InvalidSessionIdException
============================================================================== short test summary info ==============================================================================
FAILED tests/test_first.py::TestSteamPages::test_second -
selenium.common.exceptions.InvalidSessionIdException: Message: invalid session id
ERROR tests/test_first.py::TestSteamPages::test_second - selenium.common.exceptions.InvalidSessionIdException: Message: invalid session id
For the driver, I use the Singleton pattern
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
class WebDriver:
class __WebDriver:
def __init__(self, options):
self.driver = webdriver.Chrome(options=options, service=ChromeService(ChromeDriverManager().install()))
driver = None
def __init__(self, options):
if not self.driver:
WebDriver.driver = WebDriver.__WebDriver(options=options).driver

The driver is not reinitialized because of the singleton. WebDriver.driver is initialized before first test, so in the next time setup is running if not self.driver is False, because self.driver is not None anymore.
If you want to initialize the WebDriver every test don't use singleton
#pytest.fixture
def get_webdriver(get_chrome_options):
options = get_chrome_options
driver = webdriver.Chrome(options=options, service=ChromeService(ChromeDriverManager().install()))
return driver
If you do want to initialize the WebDriver only once you can change fixtures scope to 'class' (for all tests in TestSteamPages) or even move it to conftest.py with scope 'session' for one driver instance for all the test. Note that in this case all the fixtures need to have scope='class'
#pytest.fixture(scope='class')
def setup(request, get_webdriver):
driver = get_webdriver
if request.cls is not None:
request.cls.driver = driver
yield driver
driver.close()

Related

In python project(with page object) conftest file doesn't get driver from pages

I try to create the project with page object(python 3.9) and my conftest.py file doesn't receive any driver from its pages.
Any page has _init constructors,but in the conftest.py file we can see that no driver is given.
I will be glad if you have any conclusions...
Functions of conftest.py
import time
import pytest as pytest
from selenium import webdriver
from src.pom import settings
from src.pom.pages.BasePage import BasePage
from src.pom.pages.LoginPage import LoginPage
# from pytest_lazyfixture import lazy_fixture
driver = None
def pytest_addoption(parser):
# parser.addoption("--browser", action="store", default="chrome")
parser.addoption("--browser", action="store", default=settings.browser)
parser.addoption("--env", action="store", default=settings.env)
#pytest.fixture()
# #lazy_fixture
def getBrowser(request):
_browser = request.config.getoption("--browser")
return _browser
#pytest.fixture()
def getDriver(request, getBrowser):
global driver
print("browser from getBrowser method - " + getBrowser)
if getBrowser == "chrome":
driver = webdriver.Chrome("C:\\Tools\\chromedriver\\chromedriver.exe")
driver.get(settings.url)
driver.implicitly_wait(20)
driver.maximize_window()
request.cls.basePage = BasePage(driver)
request.cls.loginPage = LoginPage(driver)
request.cls.driver = driver
# yield request.cls.driver
yield driver
time.sleep(2)
driver.quit()
Functions from BasePage(as example):
class BasePage:
def __int__(self,driver):
self.driver = driver
self.wait = WebDriverWait(self.driver, 10)
def do_click(self, by_locator):
# self.wait.until(EC.visibility_of_element_located(by_locator)).click()
self.wait.until(EC.presence_of_element_located(by_locator)).click()
Functions from LoginPage(as example):
from selenium.webdriver.common.by import By
from src.pom.pages.BasePage import BasePage
class LoginPage(BasePage):
def __int__(self, driver):
super().__int__(driver)
self.driver = driver
EMAIL = (By.ID, "login-email")
PASSWORD = (By.ID, "login-password")
LOGIN_BUTTON = (By.CSS_SELECTOR, "div [type='submit']")
CAMPUS_LOGO = (By.CSS_SELECTOR, ".logo")
Eventually I get the error:
request.cls.basePage = BasePage(driver)
E TypeError: BasePage() takes no arguments
I have tried to work with this structure of page object in python but I failed. I have expected to run tests that began to write.

Selenium webdriver not recognized as webdriver

So I have this test and fixture:
#pytest.fixture()
def init_driver():
return webdriver.Chrome(executable_path=TestParams.CHROME_EXECUTABLE_PATH)
#pytest.mark.usefixtures('init_driver')
def test_1():
driver = init_driver
login(driver)
In this case my driver type is <class 'function'> so I cannot use its function for example get and got this error:
AttributeError: 'function' object has no attribute 'get'
When I change it to this:
#pytest.mark.usefixtures('init_driver')
def test_1():
login(webdriver.Chrome(executable_path=TestParams.CHROME_EXECUTABLE_PATH))
My driver type is <selenium.webdriver.chrome.webdriver.WebDriver (session="4ca3cb8f5bcec7a7498a65bfe5a2ea81")>
And all works fine.
What I am doing wrong ?
I fixed the syntax and changed it so that it runs without your hidden methods and variables such as login() and TestParams:
import pytest
from selenium import webdriver
#pytest.fixture()
def init_driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_1(init_driver):
driver = init_driver
print(type(driver))
Running that prints:
<class 'selenium.webdriver.chrome.webdriver.WebDriver'>

DeprecationWarning with selenium/geckodriver

What does the DeprecationWarning mean?
Also it seems like if i delete the "elem" functions it kind of works but when the chrome tab opens with the link it immediatelly closes again
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import pyautogui
import unittest
import time
class PythonOrgSearch(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Chrome(
executable_path=r'C:\Users\iwanh\Desktop\Geckodriver\geckodriver.exe')
def test_search_in_python_org_true(self):
driver = self.driver
driver.get("https://e-learning.nyc.gr/login/index.php")
self.assertIn("Python", driver.title)
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("pycon")
time.sleep(3)
elem.send_keys(Keys.RETURN)
self.assertNotIn("No results found.", driver.page_source)
def test_search_in_python_org_false(self):
driver = self.driver
driver.get("http://www.python.org")
self.assertIn("Python", driver.title)
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("ljueragiuhoerg")
elem.send_keys(Keys.RETURN)
time.sleep(2)
self.assertIn("No results found.", driver.page_source)
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
unittest.main()
Outputs of code
Output 1
Output 2
With selenium4 as the key executable_path is deprecated you have to use an instance of the Service() class along with ChromeDriverManager().install() command as below.
Pre-requisites
Ensure that: Selenium is upgraded to v4.0.0
Solutions:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
driver.get("https://www.google.com")

Pytest fixture not able to quit the test after execution in python pytest

I am using below fixture in conftest.py for open and close browser :
conftest.py:
#pytest.fixture(scope='session')
def browser():
print("Setting up webdriver connection...")
global driver
if driver is None:
profile = webdriver.FirefoxProfile()
profile.accept_untrusted_certs = True
profile.set_preference("network.proxy.type",1)
driver = webdriver.Firefox(firefox_profile=profile)
driver.maximize_window()
yield driver
driver.quit()
print("Webdriver connection closed..")
In my Test Class I have below step test_login :
def test_login(self, browser):
login_page = pagep.LoginPage(browser)
login_page.login('hptest376#gmail.com', "test12345*")
login_page.login('user', "pwd")
assert "Sum" in browser.title
And after test_login I have one more test step in my Test Class:
def test_ppt(a):
a=12
print a
Issue : I am getting error in fixture at driver.quit() and browser is not closing.
If I remove the "test_ppt(a)" step after "test_login(self, browser)" then the test runs fine.
Want to know what i need to update in my fixture "browser()" so that driver.quit() is executed.
Changed the scope of fixture to 'function' and the test is passing now.
#pytest.fixture(scope='function')
def browser():

Selenium Python: What are the errors in my code?

i would like to know why this code opens mozilla twice, and why it doesn´t close it when finishes. Furthermore, i don´t understand 100% why login is a class with a function, and not a function directly.
> import unittest
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginDetails(object):
def __init__ (self):
self.driver = webdriver.Firefox()
def logindetails(self, username, password):
driver = self.driver
driver.maximize_window()
driver.get("https://miclaro.claro.com.ar/")
driver.implicitly_wait(30)
driver.find_element_by_id("_58_login_movil").send_keys(username)
driver.find_element_by_id("_58_password_movil").send_keys(password)
driver.find_element_by_id("btn-home-login").click()
# Login Success
class TestLogin(unittest.TestCase):
def setUp(self):
self.ld = LoginDetails()
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
def test_sr_Login(self):
self.ld.logindetails("user", "pass")
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
Thank you very much!
This is because you instantiate webdriver twice - once inside the TestCase and once inside the LoginDetails class.
Why the other answer is not entirely correct
The WebDriver should not be controlled by the LoginDetails class in this case. LoginDetails class is very close to a Page Object notation representation and, hence, should be given the driver "from outside". Plus, opening browser in one class and closing it in the other is making the code close to "Spaghetti".
Better solution
Control the webdriver from the TestCase class and "share" with the LoginDetails:
import unittest
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginDetails(object):
def __init__ (self, driver):
self.driver = driver
def logindetails(self, username, password):
driver = self.driver
driver.maximize_window()
driver.get("https://miclaro.claro.com.ar/")
driver.implicitly_wait(30)
driver.find_element_by_id("_58_login_movil").send_keys(username)
driver.find_element_by_id("_58_password_movil").send_keys(password)
driver.find_element_by_id("btn-home-login").click()
class TestLogin(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.ld = LoginDetails(self.driver)
def test_sr_Login(self):
self.ld.logindetails("user", "pass")
def tearDown(self):
self.driver.close()
Firefox opens twice
In your test self.ld = LoginDetails() runs the __init__ function of LoginDetails() which in turn runs webdriver.Firefox() then you issue the same in the next line in the test case. That is why Firefox opens twice.
Firefox does not close
For the same reason as above Firefox is not closed. The tearDown of your test case only closes the instance of webdriver.Firefox() defined in the test case itself not the one opened via the __init__ function of the class.
Why LoginDetails is a class
LoginDetails is a class in this case to keep webdriver.Firefox() persistent throughout your code. If it would be a function you would open one Firefox session each time you run the function. Unless you specify webdriver.Firefox() outside the function and then pass it to the function.
Corrected Code
The following code uses the class functionality:
import unittest
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginDetails(object):
def __init__ (self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
def logindetails(self, username, password):
self.driver.maximize_window()
self.driver.get("https://miclaro.claro.com.ar/")
self.driver.implicitly_wait(30)
self.driver.find_element_by_id("_58_login_movil").send_keys(username)
self.driver.find_element_by_id("_58_password_movil").send_keys(password)
self.driver.find_element_by_id("btn-home-login").click()
def __del__(self):
''' ADDED based on comment by alecxe '''
self.driver.close()
class TestLogin(unittest.TestCase):
def setUp(self):
self.ld = LoginDetails()
def test_sr_Login(self):
self.ld.logindetails("user", "pass")
def tearDown(self):
# driver is closed by LoginDetails
pass
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()

Categories