Comprehensive Notes: Advanced REST API Test Automation
Table of Contents
- Introduction
- Dynamic JSON Payload Construction
- Parameterizing Tests with Multiple Data Sets
- Using External JSON Files as Payload
- Handling File Attachments in API Requests
- OAuth 2.0 Authentication
- Jira API Integration
- Best Practices & Interview Questions
- Quick Revision Cheat Sheet
Introduction
In real-world API testing, you often need to:
- Send dynamic data instead of hardcoded values
- Test with multiple data sets (parameterization)
- Load payloads from external files (JSON, Excel)
- Upload files (screenshots, PDFs) via API
- Authenticate using OAuth 2.0
This guide covers these advanced strategies using REST Assured and TestNG.
Dynamic JSON Payload Construction
The Problem with Hardcoded Payloads
// Hardcoded - will always create the same book
String body = "{\"name\":\"Learn Appium\",\"isbn\":\"bcd\",\"aisle\":\"227\"}";- Running the test twice will fail because the book already exists
- Cannot test different data sets without code changes
Solution: Build Payload Dynamically Using Variables
Step 1: Create a method that accepts parameters
public static String Addbook(String isbn, String aisle) {
return "{\n" +
"\"name\":\"Learn Appium Automation with Java\",\n" +
"\"isbn\":\"" + isbn + "\",\n" +
"\"aisle\":\"" + aisle + "\",\n" +
"\"author\":\"John foe\"\n" +
"}";
}Step 2: Use string concatenation correctly
// Pattern for inserting variables into JSON strings:
// "prefix" + variable + "suffix"
"\"isbn\":\"" + isbn + "\","
// ↑ ↑ ↑
// close var open
// string stringStep 3: Call from test
java
@Test
public void addBook() {
Response resp = given()
.header("Content-Type", "application/json")
.body(payLoad.Addbook("ABC123", "4567")) // dynamic values
.post("/Library/Addbook.php");
}Best Practice: Always delete the resource after testing (cleanup) to maintain test idempotency.
Parameterizing Tests with Multiple Data Sets
TestNG DataProvider Annotation
Use @DataProvider to run the same test with multiple data sets.
@DataProvider(name = "BooksData")
public Object[][] getData() {
return new Object[][] {
{"isbn1", "aisle1"},
{"isbn2", "aisle2"},
{"isbn3", "aisle3"}
};
}How It Works:
- Each inner array represents one test iteration
- Test method receives parameters in the same order
- Test runs once for each array
Complete Example
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.*;public class DynamicJsonTest { @Test(dataProvider = "BooksData")
public void addBook(String isbn, String aisle) {
baseURI = "http://216.10.245.166";
Response resp = given()
.header("Content-Type", "application/json")
.body(payLoad.Addbook(isbn, aisle))
.post("/Library/Addbook.php")
.then().assertThat().statusCode(200)
.extract().response();
String id = JsonPath.from(resp.asString()).getString("ID");
System.out.println("Created Book ID: " + id);
} @DataProvider(name = "BooksData")
public Object[][] getData() {
return new Object[][] {
{"ojfwty", "9363"},
{"cwetee", "4253"},
{"okmfet", "533"}
};
}
}
Important Rules:
RuleExplanationSame number of argumentsTest method parameters must match array elements countSame data typesParameter types must match the data types in arrayReturn type Object[][]DataProvider must return 2D Object arrayName matching@Test(dataProvider="name") must match @DataProvider(name="name")
Real-World Scenario: Adding 20 Books in One Click
When your company needs to add multiple books to a database, DataProvider allows you to:
- Store all book data in a 2D array
- Run the test once and add all 20 books automatically
- No need to write 20 separate tests
Using External JSON Files as Payload
Why Use External Files?
- Developers may share JSON files for testing
- Keeps test code clean
- Useful for static payloads that don’t change
Implementation
import java.nio.file.Files;
import java.nio.file.Paths;public class StaticJsonTest {
@Test
public void addBook() throws IOException {
baseURI = "http://216.10.245.166";
Response resp = given()
.header("Content-Type", "application/json")
.body(GenerateStringFromResource("C:\\path\\to\\Addbookdetails.json"))
.post("/Library/Addbook.php");
}
public static String GenerateStringFromResource(String path) throws IOException {
// Read all bytes from file, convert to String
return new String(Files.readAllBytes(Paths.get(path)));
}
}Step-by-Step Breakdown:
Files.readAllBytes(Paths.get(path))→ reads file as byte arraynew String(byteArray)→ converts bytes to String- Pass the String to
.body()
Note: This approach works only for static payloads. For dynamic values, use the concatenation or DataProvider methods.
Handling File Attachments in API Requests
Common Use Case: Uploading Bug Screenshots to Jira
When UI automation tests fail, automatically create a Jira bug with screenshot attachment.
REST Assured Multipart Upload
import java.io.File;given()
.pathParam("key", issueId)
.header("X-Atlassian-Token", "no-check")
.header("Authorization", "Basic " + encodedCredentials)
.multiPart("file", new File("/path/to/screenshot.jpg"))
.post("rest/api/3/issue/{key}/attachments")
.then().assertThat().statusCode(200);Key Points for Multipart:
ElementDescription.multiPart(key, fileObject)Specifies file attachmentnew File(path)File object pointing to actual fileX-Atlassian-Token: no-checkRequired for Jira attachments (prevents CSRF)Content-Type auto-setREST Assured automatically sets multipart/form-data
Common Interview Question:
Q: How do you upload a file using REST Assured?
A: Use the .multiPart() method with a File object. Example:
.multiPart("file", new File("C:\\screenshot.png"))OAuth 2.0 Authentication
Why OAuth?
In microservices architecture, backend services need to communicate securely without exposing user credentials.
Get Deshmukhaniketpradip’s stories in your inbox
Join Medium for free to get updates from this writer.
Scenario:
- User logs into banking app (provides username/password once)
- Homepage makes an “Account Overview” API call
- This call internally calls: Balance API, Credit Card API, Profile API
Problem: Passing user credentials to every internal API is insecure.
Solution: OAuth 2.0 — an authorization framework where services use access tokens instead of credentials.
OAuth Architecture
┌─────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Client │────▶│ Authorization │────▶│ Resource │
│ (App/API) │ │ Server │ │ Server │
└─────────────┘ └─────────────────┘ └──────────────┘
│ │ │
│ 1. Client ID + │ │
│ Client Secret │ │
│ ─────────────────▶│ │
│ │ │
│ 2. Access Token │ │
│ ◀─────────────────│ │
│ │ │
│ 3. Token + Request │ │
│ ─────────────────────────────────────────▶│
│ │ │
│ 4. Response │ │
│ ◀─────────────────────────────────────────│OAuth Grant Types
Grant TypeWhat You SendUse CaseClient Credentialsclient_id + client_secretServer-to-server communicationPasswordusername + passwordTrusted first-party appsAuthorization CodeAuthorization code (more complex)Third-party apps (e.g., "Login with Google")
Automating OAuth Client Credentials with REST Assured
Step 1: Get Access Token
String tokenResponse = given()
.formParams("client_id", "692183103107-p0m7ent2hk7suguv4vq22hjcfhcr43pj.apps.googleusercontent.com")
.formParams("client_secret", "erZOWM9g3UtwNRj340YYaK_W")
.formParams("grant_type", "client_credentials")
.formParams("scope", "trust")
.post("https://rahulshettyacademy.com/oauthapi/oauth2/resourceOwner/token")
.asString();JsonPath js = new JsonPath(tokenResponse);
String accessToken = js.getString("access_token");Step 2: Use Token to Access Protected Resource
String coursesResponse = given()
.queryParam("access_token", accessToken)
.get("https://rahulshettyacademy.com/oauthapi/getCourseDetails")
.asString();Important Notes:
- Use
.formParams()forapplication/x-www-form-urlencoded(OAuth token endpoint) - Use
.queryParam()when token is sent as URL parameter - Token has expiration — handle token refresh in real projects
Jira API Integration
Prerequisites: Create Jira Account and API Token
- Create a free Jira Cloud account (up to 10 users)
- Go to Account Settings → Security → API Tokens
- Generate a token (e.g.,
RSToken...) - Copy token immediately (won’t be shown again)
Basic Authentication with Base64 Encoding
Jira expects: Authorization: Basic base64(email:api_token)
Generate Base64 on Mac/Linux:
echo -n "your-email@example.com:API_TOKEN" | base64Generate Base64 on Windows (PowerShell):
[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("email:token"))Or use online tools for simplicity.
Jira API Endpoints
ActionMethodEndpointCreate IssuePOST/rest/api/3/issueAdd AttachmentPOST/rest/api/3/issue/{issueId}/attachmentsGet IssueGET/rest/api/3/issue/{issueId}
Create Issue (Bug) — Complete Code
java
import static io.restassured.RestAssured.*;
import io.restassured.path.json.JsonPath;public class JiraBugTest {
public static void main(String[] args) {
baseURI = "https://your-domain.atlassian.net/";
// 1. Create Issue
String createResponse = given()
.header("Content-Type", "application/json")
.header("Authorization", "Basic " + base64EncodedToken)
.body("{\n" +
" \"fields\": {\n" +
" \"project\": {\"key\": \"SCRUM\"},\n" +
" \"summary\": \"Button not working - automation\",\n" +
" \"issuetype\": {\"name\": \"Bug\"}\n" +
" }\n" +
"}")
.post("rest/api/3/issue")
.then().assertThat().statusCode(201)
.extract().response().asString();
JsonPath js = new JsonPath(createResponse);
String issueId = js.getString("id");
System.out.println("Created Issue ID: " + issueId);
// 2. Add Attachment
given()
.pathParam("key", issueId)
.header("X-Atlassian-Token", "no-check")
.header("Authorization", "Basic " + base64EncodedToken)
.multiPart("file", new File("C:\\screenshot.png"))
.post("rest/api/3/issue/{key}/attachments")
.then().assertThat().statusCode(200);
}
}Understanding the Headers
HeaderValuePurposeContent-Typeapplication/jsonFor create issue (JSON body)AuthorizationBasic <base64>Authentication (required for all calls)X-Atlassian-Tokenno-checkRequired for attachments (prevents CSRF)
Path Parameters in REST Assured
// Instead of hardcoding: "rest/api/3/issue/10013/attachments"
// Use pathParam for dynamic values:
.pathParam("issueId", issueId)
.post("rest/api/3/issue/{issueId}/attachments")
// Curly braces {} indicate a path parameterBest Practices & Interview Questions
Best Practices
- Always clean up test data — Delete created resources after test
- Use DataProvider for multiple data sets — Avoid code duplication
- Store credentials securely — Use environment variables, never hardcode
- Handle token expiration — Implement refresh token logic for long-running tests
- Log requests/responses — Use
.log().all()during development - Validate status codes — Always assert expected status (200, 201, etc.)
Common Interview Questions
Q1: How do you handle dynamic payloads in REST Assured?
A: Use string concatenation or a method that accepts parameters and returns the JSON string.
Q2: What is TestNG DataProvider?
A: An annotation that returns a 2D array of data. Test method runs once for each inner array, receiving array elements as parameters.
Q3: How do you upload a file using REST Assured?
A: Use .multiPart(key, new File(path)). REST Assured automatically sets multipart/form-data content type.
Q4: Explain OAuth 2.0 client credentials grant flow.
A: Client sends client_id and client_secret to authorization server, receives access token, then uses token to access protected resources.
Q5: How do you authenticate Jira API?
A: Basic authentication with Base64-encoded email:api_token. Generate API token from Jira account settings.
Q6: Difference between queryParam, formParam, and pathParam?
A:
queryParam→ URL query string (?key=value)formParam→ Form data in body (application/x-www-form-urlencoded)pathParam→ Replaces{variable}in URL path
Quick Revision Cheat Sheet
Dynamic Payload
// Method returning dynamic JSON
public static String Addbook(String isbn, String aisle) {
return "{\"isbn\":\"" + isbn + "\",\"aisle\":\"" + aisle + "\"}";
}DataProvider Pattern
@DataProvider(name = "data")
public Object[][] getData() {
return new Object[][]{{"val1","val2"}, {"val3","val4"}};
}
@Test(dataProvider = "data")
public void test(String p1, String p2) { ... }Read JSON from File
String content = new String(Files.readAllBytes(Paths.get("file.json")));File Upload (Multipart)
.multiPart("file", new File("path/to/file"))OAuth Client Credentials
String token = given()
.formParams("client_id", id, "client_secret", secret,
"grant_type", "client_credentials")
.post(tokenUrl).jsonPath().getString("access_token");Jira Create Issue
.header("Authorization", "Basic " + base64)
.header("Content-Type", "application/json")
.body("{\"fields\":{\"project\":{\"key\":\"SCRUM\"},\"summary\":\"Bug\",\"issuetype\":{\"name\":\"Bug\"}}}")
.post("/rest/api/3/issue")Jira Add Attachmen
.header("X-Atlassian-Token", "no-check")
.multiPart("file", new File("screenshot.png"))
.pathParam("id", issueId)
.post("/rest/api/3/issue/{id}/attachments")REST Assured Static Imports
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;Common Status Codes
CodeMeaning
200 OK
201 Created
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
These notes cover all key concepts from the advanced API testing lectures. Practice with the provided Postman collections and code examples for hands-on learning.





