Search code examples
python-3.xseleniumselenium-webdriverpytestpageobjects

Selenium Pytest missing argument in method


I am trying out the page object design for web testing using seleium and pytest. I keep getting this TypeError admin_login() missing 1 required positional argument: 'driver' in test_startup.py I dont really understand what is wrong. Here is the code and files

test_startup.py

from page_objects.login_page import LoginPage
import pytest
from selenium import webdriver

@pytest.fixture(scope='class')
def chrome_driver_init(request):
    chrome_driver = webdriver.Chrome('chrome_driver_path')
    chrome_driver.get('https://url')
    chrome_driver.find_element_by_id('details-button').click()
    chrome_driver.find_element_by_id('proceed-link').click()
    request.cls.driver = chrome_driver

yield
chrome_driver.close()

@pytest.mark.usefixtures('chrome_driver_init')
class TestStartUp:
    def test_login(self):
        driver = self.driver

        LoginPage.admin_login(driver)

login_page.py

import extensions.tools as tools
from selenium.webdriver.common.by import By
import pytest

class LoginPage(object):
    def __init__(self, driver):
        self.driver = driver

    def login(self, driver, username, password):
        tools.find_element_and_send_keys(driver, self.Locators.username, username)
        tools.find_element_and_send_keys(driver, self.Locators.password, password)
        tools.find_element_and_click(driver, self.Locators.submitButton)

    def admin_login(self, driver):
        self.login(driver, '#####', '######')

        assert 'Admin Menu' in driver.page_source

    class Locators:
        submitButton = (By.NAME, 'login')
        username = (By.NAME, 'j_username')
        password = (By.NAME, 'j.password')

The stuff from tools.py are just some functions I made to make it easier to find elements basically just a return various forms of driver.find_element(By.by, 'text'). They have not been giving me an issues yet


Solution

  • Thanks to Jortega for pointing me in the right direction. The solution ended up being to change the line in test_startup.py to read

    LoginPage.adming_login(LoginPage, driver)
    

    simply adding self required me to go back and change all self.[] to LoginPage.[] in the login_page.py file

    Adding it to the method when called was easier. That being said I personally hate the way this looks. Seeing LoginPage typed twice in the same line. Is there a better more pleasing way to write this?

    EDIT: So I have learned a lot in the past year and just wanted to add a much better solution. Essentially I was stupid and should have been declaring a instance of the page object instead of just calling the class.

    login_page = LoginPage(driver)
    login_page.admin_login()
    

    To make it even better declare all page objects in the pytest fixture

    from page_objects.login_page import LoginPage
    import pytest
    from selenium import webdriver
    
    @pytest.fixture(scope='class')
    def chrome_driver_init(request):
        chrome_driver = webdriver.Chrome('chrome_driver_path')
        chrome_driver.get('https://url')
        chrome_driver.find_element_by_id('details-button').click()
        chrome_driver.find_element_by_id('proceed-link').click()
        request.cls.driver = chrome_driver
        request.cls.login_page(chrome_driver)
        yield
        chrome_driver.close()
    
    @pytest.mark.usefixtures('chrome_driver_init')
    class TestStartUp:
        def test_login(self):
    
            self.login_page.admin_login()