Home > Software design >  Pytest collection warning due to __init__ constructor
Pytest collection warning due to __init__ constructor

Time:10-13

I've been teaching myself test automation with Pytest and Selenium web driver. All my test functions were in a single file called test_web.py, which is in a directory named tests. I split all the functions up and placed them in their own files in a separate directory called TestCases. For example, this is what TestCase_AddStar.py looks like:

from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.alert import Alert
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

class Tags:
    starSysButton = '//li[button][1]'
    addStarButton = "//button[contains(text(), 'Add Star')]"
    starNameField = "//input[1]"
    modalAddButton = f"(//button[contains(text(), 'Add')])[2]"
    starPath = f"//td[contains(text(),'Bob')]"
    modalClose = "//button[@class='btn-close']"

class TestCase_AddStar():

    URL = 'http://localhost:4200'

    def __init__(self, browser):
        self.browser = browser

    def load(self):
        self.browser(self.URL)

    def test_addStar(self, browser):

        starElement = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.starSysButton)))
        starElement.click()

        newStarElement = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.addStarButton)))
        newStarElement.click()

        enterStarName = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.starNameField)))
        enterStarName.send_keys("Bob")

        clickAddButton = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.modalAddButton)))
        clickAddButton.click()

        WebDriverWait(browser, 5).until(EC.alert_is_present())
        alert = Alert(browser)
        alert.accept()

        closeModal = WebDriverWait(browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.modalClose)))
        closeModal.click()

        results = WebDriverWait(browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.starPath)))
            
        if results:
            assert results

And this is what the driver, test_web.py. looks like now:

import pytest
from TestCases.TestCase_AddStar import TestCase_AddStar
from selenium.webdriver import Firefox

@pytest.fixture
def browser():
    driver = Firefox()
    driver.implicitly_wait(10)
    yield driver
    driver.quit()

def basic_test(browser):

    add_star = TestCase_AddStar(browser)
    add_star.load()

When I run the test, I get a collection warning:

PytestCollectionWarning: cannot collect test class 'TestCase_AddStar' because it has a init constructor (from: tests/test_web.py) class TestCase_AddStar():

I've tried adding the line "test = False" to the class TestCase_AddStar, I've tried renaming the file so that it doesn't have the word "test" in its filename, and I've tried to do away with the init constructor altogether. Nothing has worked. I either get a collection warning, the test is skipped outright, or the test times out because the browser doesn't load.

Does anyone have any advice?

Edit 1

TestCase_AddStar.py

class TestCase_AddStar():

    URL = 'http://localhost:4200'

    @pytest.fixture
    def load_browser(self, browser):
        browser(self.URL)
        yield
    
    def test_addStar(self, load_browser):

        starElement = WebDriverWait(load_browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.starSysButton)))
        starElement.click()

        newStarElement = WebDriverWait(load_browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.addStarButton)))
        newStarElement.click()

        enterStarName = WebDriverWait(load_browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.starNameField)))
        enterStarName.send_keys("Bob")

        clickAddButton = WebDriverWait(load_browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.modalAddButton)))
        clickAddButton.click()

        WebDriverWait(load_browser, 5).until(EC.alert_is_present())
        alert = Alert(load_browser)
        alert.accept()

        closeModal = WebDriverWait(load_browser, 10).until(EC.element_to_be_clickable((By.XPATH, Tags.modalClose)))
        closeModal.click()

        results = WebDriverWait(load_browser, 10).until(EC.presence_of_element_located((By.XPATH, Tags.starPath)))
            
        if results:
            assert results

test_web.py

import pytest
from TestCases.TestCase_AddStar import TestCase_AddStar
from selenium.webdriver import Firefox

@pytest.fixture
def browser():
    driver = Firefox()
    driver.implicitly_wait(10)
    yield driver
    driver.quit()

TestCase_AddStar()

I get the following error in traceback:

@pytest.fixture
def load_browser(self, browser):
browser(self.URL)
E       TypeError: 'WebDriver' object is not callable

Edit 2

Added .get() as recommended below. But I've got a new error now.

AttributeError command prompt

CodePudding user response:

As you have noticed, a test cannot have an argument in __init__ because it is created by pytest automatically. As far as I understand, you just want to load a specific URL in your specific test class - this is something that can be done in a fixture, too. Just derive the fixture from your browser fixture and add the load function:

class TestCase_AddStar:

    URL = 'http://localhost:4200'

    @pytest.fixture
    def load_browser(self, browser):
        browser.get(self.URL)
        yield browser

    def test_addStar(self, load_browser):
        ...

Note that you don't need the __init__ method and the basic_test function (which will not work with pytest anyway). You can add a similar derived fixture into each test class with another URL parameter.

Your derived fixture will be equivalent to:

@pytest.fixture
def load_browser(self):
    driver = Firefox()
    driver.implicitly_wait(10)
    driver.get(self.url)
    yield driver
    driver.quit()

(this is just for illustration how the derived fixture would work)

  • Related