Intermediate
Chapter 12 · 11 min read
Data-Driven Testing
Run the same test with multiple data sets using parameterization, CSV files, JSON fixtures, and dynamic test generation.
Data-Driven API Testing
Data-driven testing runs the same test logic with multiple inputs and expected outputs. Instead of writing separate tests for each scenario, you define a dataset and let the framework iterate through it.
Approaches
You can parameterize tests with inline data, external JSON/CSV files, or dynamically generated test data. Each approach has trade-offs between simplicity, maintainability, and flexibility.
When to Use
Data-driven testing shines when you need to validate the same endpoint with many input combinations: different user types, various field values, boundary conditions, or locale-specific data.
data-driven.test.js
// Inline parameterized tests
const testCases = [
{ id: 1, expectedUserId: 1 },
{ id: 5, expectedUserId: 1 },
{ id: 11, expectedUserId: 2 },
{ id: 51, expectedUserId: 6 },
{ id: 100, expectedUserId: 10 }
];
for (const tc of testCases) {
const resp = await fetch(
`https://jsonplaceholder.typicode.com/posts/${tc.id}`
);
const post = await resp.json();
console.assert(
post.userId === tc.expectedUserId,
`Post ${tc.id} should belong to user ${tc.expectedUserId}`
);
}
console.log(`Validated ${testCases.length} posts`);
// Data from external JSON
const userTests = [
{ name: 'Leanne Graham', email: 'Sincere@april.biz' },
{ name: 'Ervin Howell', email: 'Shanna@melissa.tv' },
{ name: 'Clementine Bauch', email: 'Nathan@yesenia.net' }
];
const usersResp = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await usersResp.json();
for (const tc of userTests) {
const user = users.find(u => u.name === tc.name);
console.assert(user, `User "${tc.name}" should exist`);
console.assert(user.email === tc.email, `Email should match for ${tc.name}`);
}
// CRUD test with multiple payloads
const createPayloads = [
{ title: 'First Post', body: 'Content 1', userId: 1 },
{ title: 'Second Post', body: 'Content 2', userId: 2 },
{ title: 'With Unicode: cafe\u0301', body: '\u2603', userId: 3 }
];
for (const payload of createPayloads) {
const resp = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
console.assert(resp.status === 201, `Create failed for: ${payload.title}`);
}
console.log('All data-driven tests passed!');
DataDrivenTest.java
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.Assert;
public class DataDrivenTest {
private static final String BASE = "https://jsonplaceholder.typicode.com";
@DataProvider(name = "postData")
public Object[][] postData() {
return new Object[][] {
{ 1, 1 }, // postId, expectedUserId
{ 5, 1 },
{ 11, 2 },
{ 51, 6 },
{ 100, 10 }
};
}
@Test(dataProvider = "postData")
public void testPostBelongsToUser(int postId, int expectedUserId) {
given().baseUri(BASE)
.when().get("/posts/" + postId)
.then()
.statusCode(200)
.body("userId", equalTo(expectedUserId));
}
@DataProvider(name = "userData")
public Object[][] userData() {
return new Object[][] {
{ "Leanne Graham", "Sincere@april.biz" },
{ "Ervin Howell", "Shanna@melissa.tv" },
{ "Clementine Bauch", "Nathan@yesenia.net" }
};
}
@Test(dataProvider = "userData")
public void testUserDetails(String name, String email) {
given().baseUri(BASE)
.queryParam("name", name)
.when().get("/users")
.then()
.statusCode(200)
.body("[0].email", equalTo(email));
}
@DataProvider(name = "createPayloads")
public Object[][] createPayloads() {
return new Object[][] {
{ "First Post", "Content 1", 1 },
{ "Second Post", "Content 2", 2 },
{ "Unicode Post \u2603", "Body \u2603", 3 }
};
}
@Test(dataProvider = "createPayloads")
public void testCreatePost(String title, String body, int userId) {
String payload = String.format(
"{\"title\": \"%s\", \"body\": \"%s\", \"userId\": %d}",
title, body, userId
);
given().baseUri(BASE)
.contentType("application/json")
.body(payload)
.when().post("/posts")
.then()
.statusCode(201)
.body("title", equalTo(title));
}
}
test_data_driven.py
import requests
import pytest
BASE_URL = 'https://jsonplaceholder.typicode.com'
# Inline parameterization with pytest
@pytest.mark.parametrize('post_id, expected_user_id', [
(1, 1),
(5, 1),
(11, 2),
(51, 6),
(100, 10),
])
def test_post_belongs_to_user(post_id, expected_user_id):
response = requests.get(f'{BASE_URL}/posts/{post_id}')
assert response.status_code == 200
assert response.json()['userId'] == expected_user_id
@pytest.mark.parametrize('name, email', [
('Leanne Graham', 'Sincere@april.biz'),
('Ervin Howell', 'Shanna@melissa.tv'),
('Clementine Bauch', 'Nathan@yesenia.net'),
])
def test_user_details(name, email):
response = requests.get(f'{BASE_URL}/users', params={'name': name})
users = response.json()
assert len(users) > 0
assert users[0]['email'] == email
@pytest.mark.parametrize('payload', [
{'title': 'First Post', 'body': 'Content 1', 'userId': 1},
{'title': 'Second Post', 'body': 'Content 2', 'userId': 2},
{'title': 'Unicode Post \u2603', 'body': '\u2603', 'userId': 3},
])
def test_create_post(payload):
response = requests.post(f'{BASE_URL}/posts', json=payload)
assert response.status_code == 201
assert response.json()['title'] == payload['title']
# Data from external file (conftest.py fixture)
# @pytest.fixture
# def test_data():
# import json
# with open('test_data/users.json') as f:
# return json.load(f)
API Testing
Intermediate
Data-Driven Testing
Written by PV
© 2026 All Rights Reserved