Introduction
In our REST Assured series, we’ve explored topics like REST Assured with JUnit, REST Assured with TestNG, and Request and Response Logging, equipping you with tools to create robust API tests. Now, we’ll dive into Parameterized Tests, a technique to run the same test with multiple input data sets, enhancing test coverage and efficiency. This guide demonstrates how to implement parameterized tests with REST Assured using JUnit 5 and TestNG, with practical examples and best practices. It’s designed for beginners and experienced developers, ensuring you can master data-driven API testing.
Key Point: Parameterized Tests in REST Assured allow you to execute the same test logic with different inputs, reducing code duplication and improving test scalability.
What are Parameterized Tests?
Parameterized Tests enable a single test method to run multiple times with different input parameters, ideal for testing APIs with varying inputs (e.g., different IDs, payloads, or query parameters). Both JUnit 5 and TestNG support parameterized testing:
- JUnit 5: Uses
@ParameterizedTest
with data sources like@ValueSource
,@CsvSource
, or@MethodSource
. - TestNG: Uses
@DataProvider
to supply data to@Test
methods.
REST Assured’s fluent API integrates seamlessly with these frameworks, allowing assertions on responses for each parameter set. We’ll use https://jsonplaceholder.typicode.com
for examples, testing endpoints like /posts
and /comments
to demonstrate parameterized tests with various data sources.
Setting Up for Parameterized Tests
We’ll set up a Maven project with REST Assured, JUnit 5, TestNG, and Allure for reporting, supporting both frameworks for parameterized tests. The junit-jupiter
and testng
dependencies enable JUnit 5 and TestNG, respectively.
Here’s the pom.xml
, styled with your preferred Blogger format for XML syntax highlighting:
4.0.0
com.example
rest-assured-parameterized
1.0-SNAPSHOT
11
2.27.0
1.9.22
io.rest-assured
rest-assured
5.4.0
test
org.junit.jupiter
junit-jupiter
5.10.2
test
org.testng
testng
7.10.2
test
org.hamcrest
hamcrest
2.2
test
com.fasterxml.jackson.core
jackson-databind
2.15.2
test
io.qameta.allure
allure-junit5
${allure.version}
test
io.qameta.allure
allure-testng
${allure.version}
test
org.apache.maven.plugins
maven-surefire-plugin
3.2.5
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
org.aspectj
aspectjweaver
${aspectj.version}
io.qameta.allure
allure-maven
2.12.0
${allure.version}
No additional setup is required for https://jsonplaceholder.typicode.com
. For TestNG, we’ll create a testng.xml
file later in Example 4.
Implementing Parameterized Tests
Let’s explore how to implement parameterized tests with REST Assured using JUnit 5 and TestNG.
Example 1: JUnit 5 with @ValueSource
Use @ParameterizedTest
with @ValueSource
to test multiple post IDs.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Parameterized Tests with REST Assured")
public class JUnitValueSourceTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@ParameterizedTest(name = "Test fetching post with ID {0}")
@ValueSource(ints = {1, 2, 3})
@Description("Test fetching posts with different IDs using ValueSource")
public void testGetPosts(int postId) {
given()
.log().all()
.when()
.get("/posts/{id}", postId)
.then()
.log().all()
.statusCode(200)
.body("id", equalTo(postId))
.body("userId", greaterThan(0));
}
}
Explanation:
@ParameterizedTest
: Marks the test as parameterized.@ValueSource(ints = {...})
: Provides a list of integers (post IDs).name = "..."
: Customizes the test name in reports for clarity.- Each iteration tests a different post ID, logging requests and responses.
Important: Use @ValueSource
for simple data types like integers, strings, or booleans.
Example 2: JUnit 5 with @CsvSource
Use @CsvSource
to test posts with multiple parameters (ID and expected user ID).
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Parameterized Tests with REST Assured")
public class JUnitCsvSourceTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@ParameterizedTest(name = "Test post ID {0} with user ID {1}")
@CsvSource({
"1, 1",
"11, 2",
"21, 3"
})
@Description("Test fetching posts with ID and expected user ID using CsvSource")
public void testGetPostsWithUserId(int postId, int expectedUserId) {
given()
.log().all()
.when()
.get("/posts/{id}", postId)
.then()
.log().all()
.statusCode(200)
.body("id", equalTo(postId))
.body("userId", equalTo(expectedUserId));
}
}
Explanation:
@CsvSource
: Provides comma-separated values for multiple parameters.- Each row represents a test case with post ID and expected user ID.
- Validates both the post ID and user ID in the response.
Example 3: JUnit 5 with @MethodSource
Use @MethodSource
to provide complex data for testing POST requests.
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Parameterized Tests with REST Assured")
public class JUnitMethodSourceTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
static Stream postData() {
return Stream.of(
Arguments.of("Test Post 1", "Body 1", 1),
Arguments.of("Test Post 2", "Body 2", 2),
Arguments.of("Test Post 3", "Body 3", 3)
);
}
@ParameterizedTest(name = "Test creating post with title {0}")
@MethodSource("postData")
@Description("Test creating posts with different data using MethodSource")
public void testCreatePosts(String title, String body, int userId) {
String requestBody = String.format("{\"title\": \"%s\", \"body\": \"%s\", \"userId\": %d}", title, body, userId);
given()
.header("Content-Type", "application/json")
.body(requestBody)
.log().all()
.when()
.post("/posts")
.then()
.log().all()
.statusCode(201)
.body("title", equalTo(title))
.body("userId", equalTo(userId));
}
}
Explanation:
@MethodSource
: References a static method (postData
) that returns aStream
.- Each argument set includes title, body, and user ID for a POST request.
- Tests simulate creating posts with different payloads.
Pro Tip: Use @MethodSource
for complex objects or dynamic data generation in parameterized tests.
Example 4: TestNG with @DataProvider
Use @DataProvider
to test comments with different IDs.
Create testng.xml
in the project root to run TestNG tests:
Test code:
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Parameterized Tests with REST Assured")
public class TestNGDataProviderTest {
@BeforeClass
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@DataProvider(name = "commentIds")
public Object[][] provideCommentIds() {
return new Object[][] {
{1},
{2},
{3}
};
}
@Test(dataProvider = "commentIds", description = "Test fetching comments with different IDs")
@Description("Test fetching comments with different IDs using DataProvider")
public void testGetComments(int commentId) {
given()
.log().all()
.when()
.get("/comments/{id}", commentId)
.then()
.log().all()
.statusCode(200)
.body("id", equalTo(commentId))
.body("email", notNullValue());
}
}
Explanation:
@DataProvider
: Supplies comment IDs to the test method.@Test(dataProvider = "commentIds")
: Runs the test for each ID.testng.xml
: Configures the test suite for execution.
Example 5: Parameterized Tests with Allure
Integrate parameterized tests with Allure reporting for JUnit 5.
import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Feature("Parameterized Tests with REST Assured")
public class JUnitAllureTest {
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}
@ParameterizedTest(name = "Test fetching post with ID {0}")
@ValueSource(ints = {1, 2, 3})
@Description("Test fetching posts with Allure reporting")
public void testGetPostsWithAllure(int postId) {
Response response = given()
.log().all()
.when()
.get("/posts/{id}", postId)
.then()
.log().all()
.statusCode(200)
.body("id", equalTo(postId))
.extract().response();
Allure.addAttachment("Response Body for Post ID " + postId, "application/json", response.asString(), ".json");
}
}
Explanation:
Allure.addAttachment
: Attaches the response body for each parameterized test to the Allure report.- Dynamic attachment names include the post ID for clarity.
- Run
mvn clean test
andmvn allure:serve
to view the report.
Integrating with CI/CD
Add parameterized tests to a GitHub Actions pipeline for automation, supporting both JUnit and TestNG.
Create or update .github/workflows/ci.yml
:
name: REST Assured Parameterized CI Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'temurin'
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-
- name: Run JUnit and TestNG tests
run: mvn clean test -Dsurefire.suiteXmlFiles=testng.xml
- name: Generate Allure report
run: mvn allure:report
- name: Upload Allure results
if: always()
uses: actions/upload-artifact@v4
with:
name: allure-results
path: target/allure-results
- name: Publish Allure report
if: always()
uses: simple-elf/allure-report-action@v1.7
with:
allure_results: target/allure-results
gh_pages: gh-pages
allure_report: allure-report
- name: Deploy report to GitHub Pages
if: always()
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: allure-report
Explanation:
-Dsurefire.suiteXmlFiles=testng.xml
: Runs TestNG tests alongside JUnit tests.- Publishes Allure reports to GitHub Pages for visibility.
Tips for Beginners
- Start Simple: Use
@ValueSource
or@DataProvider
for basic data sets before moving to complex sources. - Keep Data Focused: Test only necessary parameter combinations to avoid excessive test runs.
- Enable Logging: Use
log().all()
to debug failed parameterized tests. - Use Descriptive Names: Customize test names with parameters for clear reports.
Troubleshooting Tip: If a parameterized test fails, check the logs for the specific parameter set and verify the response matches the expected assertions.
What’s Next?
This post enhances our REST Assured series by introducing Parameterized Tests, a powerful tool for data-driven API testing. To continue your learning, consider exploring:
- GraphQL Testing: Testing GraphQL APIs with REST Assured.
- Advanced Allure Reporting: Customizing reports with environments and categories.
- End-to-End Testing: Combining REST Assured with Selenium for UI and API testing.