Data providers pattern in Selenium Python - Build an Automation Script
import unittest from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver = driver self.url = 'https://example.com/login' self.username_input = (By.ID, 'username') self.password_input = (By.ID, 'password') self.login_button = (By.ID, 'loginBtn') self.error_message = (By.ID, 'errorMsg') def load(self): self.driver.get(self.url) def login(self, username, password): WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located(self.username_input) ) self.driver.find_element(*self.username_input).clear() self.driver.find_element(*self.username_input).send_keys(username) self.driver.find_element(*self.password_input).clear() self.driver.find_element(*self.password_input).send_keys(password) self.driver.find_element(*self.login_button).click() def is_error_displayed(self): try: return WebDriverWait(self.driver, 5).until( EC.visibility_of_element_located(self.error_message) ) is not None except: return False class TestLogin(unittest.TestCase): @classmethod def setUpClass(cls): cls.driver = webdriver.Chrome() cls.driver.implicitly_wait(5) cls.login_page = LoginPage(cls.driver) @classmethod def tearDownClass(cls): cls.driver.quit() def data_provider(self): return [ ('validUser1', 'validPass1', True), ('invalidUser', 'invalidPass', False), ('validUser2', 'wrongPass', False) ] def test_login_with_multiple_credentials(self): for username, password, is_valid in self.data_provider(): with self.subTest(username=username, password=password): self.login_page.load() self.login_page.login(username, password) if is_valid: WebDriverWait(self.driver, 10).until( EC.url_to_be('https://example.com/dashboard') ) current_url = self.driver.current_url self.assertEqual(current_url, 'https://example.com/dashboard', f'Expected dashboard URL for valid user {username}') else: self.assertTrue(self.login_page.is_error_displayed(), f'Error message should be displayed for invalid user {username}')
This test script uses Selenium with Python's unittest framework.
We define a LoginPage class as a Page Object to handle page actions and locators. This keeps code clean and reusable.
The data_provider method returns multiple sets of username, password, and expected validity.
The test method test_login_with_multiple_credentials loops over these data sets using subTest for clear reporting.
For valid credentials, it waits until the URL changes to the dashboard and asserts it matches.
For invalid credentials, it checks if the error message is visible.
Explicit waits ensure the test waits for elements or URL changes properly, avoiding flaky tests.
Setup and teardown methods open and close the browser once for all tests.
Now add data-driven testing with 3 different inputs using unittest's subTest and parameterize the test method.
Practice
data providers pattern in Selenium Python tests?Solution
Step 1: Understand the data providers pattern
The data providers pattern allows running one test repeatedly with different sets of data.Step 2: Identify the main benefit
This helps find more bugs by testing various inputs without writing separate tests.Final Answer:
To run the same test multiple times with different input data -> Option DQuick Check:
Data providers = run test many times with different data [OK]
- Thinking data providers speed up browser loading
- Confusing data providers with automatic test creation
- Believing data providers generate reports
Solution
Step 1: Recall pytest data provider syntax
Pytest uses@pytest.mark.parametrizeto supply multiple data sets to a test function.Step 2: Match the correct syntax
@pytest.mark.parametrize('input', ['data1', 'data2']) correctly shows@pytest.mark.parametrize('input', ['data1', 'data2']).Final Answer:
@pytest.mark.parametrize('input', ['data1', 'data2']) -> Option AQuick Check:
Pytest data provider = @pytest.mark.parametrize [OK]
- Using non-existent decorators like @pytest.data_provider
- Confusing parametrize with test_data or run_with_data
- Missing quotes around parameter names
import pytest
@pytest.mark.parametrize('num', [1, 2, 3])
def test_is_positive(num):
assert num > 0Solution
Step 1: Analyze the test logic
The test asserts that the input number is greater than zero.Step 2: Check each data value
Values 1, 2, and 3 are all greater than zero, so all assertions pass.Final Answer:
All tests pass -> Option AQuick Check:
All nums > 0, so tests pass [OK]
- Assuming some inputs fail without checking values
- Thinking syntax error due to parametrize
- Ignoring that all numbers are positive
@pytest.mark.parametrize('input', [1, 2, 3])
def test_example():
assert input > 0Solution
Step 1: Check test function parameters
The test function must accept the parameter named in parametrize, here 'input'.Step 2: Identify missing parameter
The functiontest_example()has no parameters, so 'input' is undefined inside it.Final Answer:
Missing parameter in test function definition -> Option BQuick Check:
Parametrize param must match function argument [OK]
- Forgetting to add parameter to test function
- Using wrong decorator name
- Thinking input list must be strings
- Misreading assertion syntax
Solution
Step 1: Understand multiple parameter usage in parametrize
To pass multiple parameters together, use a tuple of parameter names and a list of tuples with values.Step 2: Identify correct syntax
@pytest.mark.parametrize(('username', 'password'), [('user1', 'pass1'), ('user2', 'pass2')]) def test_login(username, password): assert username and password uses@pytest.mark.parametrize(('username', 'password'), [('user1', 'pass1'), ('user2', 'pass2')]), which is correct.Final Answer:
Use tuple of parameter names and list of tuples for values -> Option CQuick Check:
Multiple params = tuple names + list of tuples [OK]
- Stacking multiple parametrize decorators incorrectly
- Passing multiple parameters in one string without tuple
- Wrong syntax with multiple lists in one decorator
Ready to prove it?
Pass the test and this pattern moves unknown
