Data-Driven Testing with Pytest: Concepts, Design, and Practical Examples
This article explains data-driven testing (DDT) concepts, design approaches, and practical implementation using the Pytest framework, covering test data creation, storage options, parameterization techniques, fixture integration, and example scripts to enhance test coverage, maintainability, and efficiency.
In automated testing, test data is essential for creating test scenarios. This article discusses how to layer an automation framework, store data, and execute tests using Pytest, providing a concrete data-driven testing solution.
Basic Concepts – Data-driven testing (DDT) repeats test steps with inputs and expected values supplied from external data sources such as Excel, CSV, YAML, data pools, ADO objects, or ODBC sources. It separates test logic from data, improves reusability, and reduces the number of test cases.
Design Idea – The MCube framework checks template cache status, fetches the latest template if needed, loads it into a view‑tree, resolves expressions and custom events, binds them, and finally renders the view. This design aims to increase page‑building efficiency from design drafts.
Practice Sharing – The Laputa framework, built on Pytest, integrates API, web, mobile, and desktop UI automation. It offers a visual web UI for configuring execution rules, linking scripts, triggering runs, and viewing results, with CI integration via Jenkins.
Parameterization Methods
Pytest provides two ways to parametrize tests:
def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None):Example using @pytest.mark.parametrize :
@pytest.mark.parametrize("test_input,expected",[("3+5",8),("2+5",7),("7*5",30)])
def test_eval(test_input,expected):
# eval evaluates the string as a Python expression
assert eval(test_input) == expectedRunning the above yields three passed tests.
Multiple Parameterization – Multiple @pytest.mark.parametrize decorators generate a Cartesian product of parameters:
@pytest.mark.parametrize("x",[1,2])
@pytest.mark.parametrize("y",[8,10,11])
def test_foo(x,y):
print(f"Test data combination x: {x} , y:{y}")This creates 2 × 3 = 6 test cases.
Fixture and Parameterization Integration – Using indirect=True passes parameters to a fixture:
# method name as parameter
test_user_data = ['Tome', 'Jerry']
@pytest.fixture(scope="module")
def login_r(request):
user = request.param
print(f"\n Login user: {user}")
return user
@pytest.mark.parametrize("login_r", test_user_data, indirect=True)
def test_login(login_r):
a = login_r
print(f"Login return value in test case: {a}")
assert a != ""The fixture receives each user name and the test runs twice.
Data Storage – Test data can be kept in YAML files. Example:
测试流程:[
{"name":"B2B普通货运输三方司机流程","senior":{"createTransJobResource":"B2B","createType":"三方","platformType":2}},
{"name":"B2B普通货运输三方司机逆向流程","senior":{"isback":"True","createTransJobResource":"B2B","createType":"三方","platformType":2}},
]A helper reads the YAML and returns separate lists for names and senior data:
def dataBuilder(key):
dires = path.join(dires, "test_data.yaml")
parameters = laputa_util.read_yaml(dires)[key]
name = []
senior = []
for item in parameters:
name.append(item['name'] if 'name' in item else '')
senior.append(item['senior'] if 'senior' in item else '')
return name, seniorThese lists are then used to parametrize a test class:
class TestRegression:
case, param = dataBuilder('测试流程')
@pytest.mark.parametrize("param", param, ids=case)
def test_regression_case(self, param):
# scheduling
res = create_trans_bill(params)
trans_job_code = res['data']['jobcode']
carrier_type = params['createType'] if params['createType'] in ('自营', '三方') else '个体'
# execution
work_info = select_trans_work_info_new(trans_job_code)
trans_work_code = work_info['trans_work_code']
if 'isback' in params and params['isback']:
execute_param.update(isBack=params['isback'])
execute_bill_core(**execute_param)
# settlement
if carrier_type != '自营':
trans_fee_code = CreateTransFeeBillBase.checkTF(trans_job_code)
receive_trans_bill_core(**bill_param)Conclusion – Applying data‑driven design in daily testing, whether manually or via scripts, improves scenario coverage, test robustness, reusability, and overall testing efficiency, delivering better ROI and quality‑effectiveness balance.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.