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
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.
To use fixture I need :
- to add fixtures in conftest.py (as I use pytest)
- to drop every hardcoded values in my ClientLogin class.
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 ===============================================================================