I'm reading on how to write proper testing suites here. So I'm trying to follow the selenium example in the docs which is in Java; I'm trying to translate it to Python since my app is written in Python.

So I translated the example like so:

class LoginPage(unittest.TestCase):
    FE_URL = os.getenv('FE_URL')
    SERVER_URL = os.getenv('SERVER_URL')
    def __init__(self, driver):
        self.selenium = driver

    def find_button_by_text(self, text):
        buttons = self.selenium.find_elements_by_tag_name("button")
        for btn in buttons:
            if text in btn.get_attribute("innerHTML"):
                return btn

    def login_page(self):
        WebDriverWait(self.selenium, MAX_WAIT).until(
            EC.presence_of_element_located((By.CLASS_NAME, "login"))
        return self.selenium
    def type_username(self, username):
        username_locator = self.selenium.find_element_by_name("user[email]")
        return self.selenium

    def type_password(self, password):
        password_locator = self.selenium.find_element_by_name("user[password]")
        return self.selenium

    def submit_login(self):
        login_locator = self.find_button_by_text("Continue")
        return self.selenium

    def submit_login_expecting_failure(self):
        return self.selenium

    def login_as(self, username, password):
        login_page = self.login_page()
        return self.submit_login()

and then the actual test is here:

class MyTest(unittest.TestCase):
    USER_NAME = os.getenv('USER_NAME')
    PASSWORD = os.getenv('PASSWORD')

    def setUpClass(cls):
        super(MyTest, cls).setUpClass()
        cls.selenium = WebDriver()
        # cls.selenium = webdriver.Firefox()
        cls.wait = WebDriverWait(cls.selenium, MAX_WAIT)
    def tearDownClass(cls):
        super(MyTest, cls).tearDownClass()

    def test_login(self):
        login_page = LoginPage(self.selenium)
        main_page = login_page.login_as(self.USER_NAME, self.PASSWORD)
        WebDriverWait(main_page, MAX_WAIT).until(
            EC.presence_of_element_located((By.LINK_TEXT, "Create alert"))

    def test_failed_login(self):
        login_page = LoginPage(self.selenium)
        page = login_page.submit_login_expecting_failure()
        alert = WebDriverWait(page, MAX_WAIT).until(
            EC.visibility_of_element_located((By.CLASS_NAME, "alert-danger"))
        self.assertIn("Invalid Email or Password", alert.text)

if __name__ == "__main__":

The test works. Did I understand this correctly that the driver is setup in the actual test class and not in the LoginPage?

Did I hide the actual mechanics of the test correctly? I am using WebDriverWait in the LoginPage class to wait till the page is loaded. I see this as kind of an assert replacement but I am not sure how else to wait for the page to have finished loading.

  • 1
    \$\begingroup\$ Do FE_URL and SERVER_URL take values that point to a publicly-accessible server you can share with us for the purposes of testing? \$\endgroup\$
    – Reinderien
    Jul 11, 2021 at 13:35
  • \$\begingroup\$ those are only localhost values, as in http://localhost:3000 and http://localhost:3001 I put them in env variables cause I thought this is how you are supposed to do it \$\endgroup\$
    – Tom
    Jul 11, 2021 at 16:22
  • 1
    \$\begingroup\$ No that's fine, you've done the right thing. It just means that I can't test your code for you. \$\endgroup\$
    – Reinderien
    Jul 11, 2021 at 16:32

1 Answer 1


I think the biggest miss here is that LoginPage, though it is perhaps fine as a test utility class, is clearly not a TestCase and should not inherit from that.

This loop:

    buttons = self.selenium.find_elements_by_tag_name("button")
    for btn in buttons:
        if text in btn.get_attribute("innerHTML"):
            return btn

should not be needed, and you should be able to write a single selector that accomplishes the same thing. Without access to your DOM I don't know what that's going to be, precisely.

Your pattern of return self.selenium isn't particularly useful, since MyTest already has a reference to its own self.selenium; so all of those functions can just be None-returns.

For Python 3 you should no longer be passing parameters into super().


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.