Skip to content

Instantly share code, notes, and snippets.

@foxmask
Last active November 10, 2019 17:43
Show Gist options
  • Save foxmask/a64c651f8802d236285aa32dfe6154b8 to your computer and use it in GitHub Desktop.
Save foxmask/a64c651f8802d236285aa32dfe6154b8 to your computer and use it in GitHub Desktop.
Blog : Python testing portal access

Python testing portal access

Hi,

Usually when we test things we create many test_myfeature_foobar.py, test_myfeature_foobar2.py and so on to test the code and the coverage.

In my case I use pytest, and we asked me to test the access of customers portal.

So I started with

  • test_customerA.py
  • test_customerB.py
  • test_customerC.py

The classical way

test_customerA.py could look like this

import unittest
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options


class ClientLogin(unittest.TestCase):

    def setUp(self):
        self.opts = Options()
        self.opts.headless = True
        self.user = "l0g1n"
        self.pwd = "passw0rd"
        self.driver = Firefox(options=self.opts)

    def test_login(self):
        self.assertTrue(self.opts.headless)  # Operating in headless mode
        driver = self.driver
        driver.get("https://www.customerA.com/login.jsp")
        self.assertIn("CustomerA - Login", driver.title)
        elem = driver.find_element_by_name("j_username")
        elem.send_keys(self.user)
        elem = driver.find_element_by_name("j_password")
        elem.send_keys(self.pwd)
        elem.send_keys(Keys.RETURN)
        WebDriverWait(driver, 10).until(EC.title_contains("CustomerA - Home"))
        self.assertIn("CustomerA - Home", driver.title)

    def tearDown(self):
        self.driver.close()


if __name__ == "__main__":
    unittest.main()

and as our software version for each customer is not the same, the HTML is not the same so I need to deal with that

so test_customerB.py could look like this

import unittest
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options


class ClientLogin(unittest.TestCase):

    def setUp(self):
        self.opts = Options()
        self.opts.headless = True
        self.user = "l0gin2"
        self.pwd = "passw0rd2"
        self.driver = Firefox(options=self.opts)

    def test_login(self):
        self.assertTrue(self.opts.headless)  # Operating in headless mode
        driver = self.driver
        driver.get("http://www.cusomerB.com/login.jsp")
        self.assertIn("customerB - Login page", driver.title)
        elem = driver.find_element_by_id("login")
        elem.send_keys(self.user)
        elem = driver.find_element_by_id("password")
        elem.send_keys(self.pwd)
        elem.send_keys(Keys.RETURN)
        WebDriverWait(driver, 10).until(EC.title_contains("customerB - Welcome"))
        self.assertIn("customerB - Welcome", driver.title)

    def tearDown(self):
        self.driver.close()


if __name__ == "__main__":
    unittest.main()

When I launch pytest on each of them, everything works fine. But as today the number of customer portal will grow up, I can't imagine to manage one test_customer.py for each of them. So comes the moment to use fixtures.

DRY way

To use fixture I need :

  • to add fixtures in conftest.py (as I use pytest)
  • to drop every hardcoded values in my ClientLogin class.

conftest.py

the content is simple, I make one class per customer, initialize a dict for each of them.

let's have a look

import pytest


class CustomerA:
    client = {}

    def __init__(self):
        self.client = {'url': 'http://www.customerA.com/',
                       'client_name': 'customer_a',
                       'login': 'l0g1n',
                       'password': 'passw0rd',
                       'find_by': 'name',
                       'home_title': 'CustomerA - Login',
                       'home_login': 'j_username',
                       'home_password': 'j_password',
                       'main_title': 'CustomerA - Home'}


class CustomerB:
    client = {}

    def __init__(self):
        self.client = {'url': 'http://www.customerB.com/',
                       'client_name': 'customer_b',
                       'login': 'l0gin2',
                       'password': 'p4ssw0rd',
                       'find_by': 'id',
                       'home_title': 'Identification',
                       'home_login': 'j_login',
                       'home_password': 'j_password',
                       'main_title': 'customerB - Welcome'}


class CustomerC:
    client = {}

    def __init__(self):
        self.client = {'url': 'http://www.customerC.com/',
                       'client_name': 'customer_c',
                       'login': 'l00gin',
                       'password': 'passw00rd',
                       'find_by': 'name',
                       'home_title': 'customerC - Login page',
                       'home_login': 'login',
                       'home_password': 'password',
                       'main_title': 'Welcome'}


def pytest_generate_tests(metafunc):
    if "portal_login" in metafunc.fixturenames:
        metafunc.parametrize("portal_login",
                             ["customerA", "customerB", "customerC"],
                             indirect=True)


@pytest.fixture
def portal_login(request):
    if request.param == 'customerA':
        return CustomerA()
    if request.param == "customerB":
        return CustomerB()
    elif request.param == "customerC":
        return CustomerC()
    else:
        raise ValueError("invalid internal test config")

the main trick here is the pytest_generate_tests function that will run the test with all the 3 fixtures we gave in parms

And finally the generic test module which will call the portal_login fixture defined in conftest.py

test_portal_access.py

import unittest
import pytest
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver import Firefox
from selenium.webdriver.firefox.options import Options

opts = Options()
opts.headless = True
driver = Firefox(options=opts)


def test_login(portal_login):
    assert opts.headless
    driver.get(portal_login.client['url'])
    assert portal_login.client['home_title'] in driver.title
    if portal_login.client['find_by'] == 'id':
        elem = driver.find_element_by_id(portal_login.client['home_field_login'])
        elem.send_keys(portal_login.client['login'])
        elem = driver.find_element_by_id(portal_login.client['home_field_password'])
        elem.send_keys(portal_login.client['password'])
    else:
        elem = driver.find_element_by_name(portal_login.client['home_field_login'])
        elem.send_keys(portal_login.client['login'])
        elem = driver.find_element_by_name(portal_login.client['home_field_password'])
        elem.send_keys(portal_login.client['password'])

    elem.send_keys(Keys.RETURN)
    WebDriverWait(driver, 10).until(EC.title_contains(portal_login.client['main_title']))
    assert portal_login.client['main_title'] in driver.title


if __name__ == "__main__":
    unittest.main()

you may have noticed I dropped the class ClientLogin for a single test function

Now I can run

pytest test_portal_access.py
test_portal_access.py ...                                                                                                                                               [100%]

============================================================================== 3 passed in 3.49s ===============================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment