Writing the same test 5 times with different data is not testing. It is copying. Data-driven testing lets you write the test once and run it with 50 different inputs. One test method. One DataProvider. Many executions.
For the capstone, we will read test data from a JSON file. This is what you do in real projects — test data lives outside the code so non-developers can update it.
[
{
"userId": 1,
"expectedPostCount": 10,
"name": "Leanne Graham"
},
{
"userId": 2,
"expectedPostCount": 10,
"name": "Ervin Howell"
},
{
"userId": 3,
"expectedPostCount": 10,
"name": "Clementine Bauch"
},
{
"userId": 7,
"expectedPostCount": 10,
"name": "Kurtis Weissnat"
},
{
"userId": 10,
"expectedPostCount": 10,
"name": "Clementina DuBuque"
}
]package com.capstone.api.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.InputStream;
public class TestDataReader {
private static final ObjectMapper mapper = new ObjectMapper();
public static JsonNode readJsonFile(String filename) {
try (InputStream is = TestDataReader.class.getClassLoader()
.getResourceAsStream("testdata/" + filename)) {
if (is == null) throw new RuntimeException("File not found: " + filename);
return mapper.readTree(is);
} catch (Exception e) {
throw new RuntimeException("Failed to read: " + filename, e);
}
}
}package com.capstone.api.tests;
import com.capstone.api.base.BaseTest;
import com.capstone.api.utils.TestDataReader;
import com.fasterxml.jackson.databind.JsonNode;
import io.qameta.allure.Epic;
import io.qameta.allure.Feature;
import io.qameta.allure.Severity;
import io.qameta.allure.SeverityLevel;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;
@Epic("JSONPlaceholder API")
@Feature("Data-Driven Tests")
public class PostDataDrivenTests extends BaseTest {
@DataProvider(name = "usersFromJson")
public Object[][] getUserData() {
JsonNode users = TestDataReader.readJsonFile("users.json");
Object[][] data = new Object[users.size()][3];
for (int i = 0; i < users.size(); i++) {
data[i][0] = users.get(i).get("userId").asInt();
data[i][1] = users.get(i).get("expectedPostCount").asInt();
data[i][2] = users.get(i).get("name").asText();
}
return data;
}
@Test(dataProvider = "usersFromJson", groups = "regression")
@Severity(SeverityLevel.NORMAL)
public void testEachUserHasExpectedPostCount(
int userId, int expectedCount, String name) {
given().spec(requestSpec)
.queryParam("userId", userId)
.when()
.get("/posts")
.then()
.statusCode(200)
.body("size()", equalTo(expectedCount))
.body("userId", everyItem(equalTo(userId)));
}
@Test(dataProvider = "usersFromJson", groups = "regression")
@Severity(SeverityLevel.NORMAL)
public void testEachUserExists(int userId, int expectedCount, String name) {
given().spec(requestSpec)
.when()
.get("/users/" + userId)
.then()
.statusCode(200)
.body("id", equalTo(userId))
.body("name", equalTo(name));
}
// Inline DataProvider for quick, small datasets
@DataProvider(name = "postIds")
public Object[][] postIdData() {
return new Object[][] {
{ 1, 1 }, // post ID, expected userId
{ 11, 2 },
{ 21, 3 },
{ 31, 4 },
{ 41, 5 },
{ 51, 6 },
{ 61, 7 },
{ 71, 8 },
{ 81, 9 },
{ 91, 10 }
};
}
@Test(dataProvider = "postIds", groups = "regression")
@Severity(SeverityLevel.NORMAL)
public void testFirstPostOfEachUserHasCorrectUserId(
int postId, int expectedUserId) {
given().spec(requestSpec)
.when()
.get("/posts/" + postId)
.then()
.statusCode(200)
.body("id", equalTo(postId))
.body("userId", equalTo(expectedUserId));
}
}Three test methods. But the first two run 5 times each (from JSON file) and the third runs 10 times (inline DataProvider). That is 20 test executions from 3 methods. This is the power of data-driven testing.
Use external files (JSON, CSV) for data that changes often or is managed by non-developers. Use inline DataProvider arrays for small, stable datasets. The postIds DataProvider above is fine inline — it maps to a fixed API structure.
Q: How do you implement data-driven testing in REST Assured?
A: We use TestNG DataProvider. For large or frequently changing datasets, the DataProvider reads from external JSON or CSV files using a utility class that wraps Jackson ObjectMapper. For small fixed datasets, we use inline Object arrays. Each DataProvider returns a 2D array where each row becomes a set of parameters for the test method. For example, our users.json has 5 user records — the test runs 5 times, once per user, validating that each user has the correct post count. Adding new test data is just adding a row to the JSON file — no code changes needed.
Key Point: Data-driven testing with DataProvider lets you write one test method and run it with many inputs. Use JSON files for large datasets and inline arrays for small ones. 3 methods can produce 20+ test executions.