Skip to content

JPostman Tutorial Part 5: TestNG Support and Fluent API Testing

David Gofman edited this page Jun 16, 2026 · 4 revisions

TestNG Support and Fluent API Testing

Overview

In this tutorial, we review the native TestNG support added to JPostman and the fluent API improvements available in release 1.1.3.

Starting with release 1.1.0, JPostman introduced native support for JUnit and TestNG. This helps reduce boilerplate code and makes API test flows cleaner, easier to read, and easier to maintain.

This tutorial continues from the secure logging flow in Part 4 and shows how to simplify the same API test by using jpostman-testng, fluent response execution, response assertions, verification, and cached access tokens.

What You Will Learn

By the end of this tutorial, you will know how to:

  • Update the JPostman Maven version to 1.1.3.
  • Replace separate TestNG, jpostman-core, and jpostman-secure dependencies with jpostman-testng.
  • Update the Maven project so the latest dependency changes are downloaded.
  • Store Postman environment values as protected values with secret(...).
  • Load secure masking rules from an INI policy file.
  • Remove unnecessary .builder().build() calls when the context resolves request values.
  • Use fluent response execution with .response(c -> ...).
  • Validate response fields before reading them with path(...).
  • Use custom assertion messages.
  • Use TestNG SoftAssert to collect multiple assertion failures.
  • Cache an access token for reuse across tests.
  • Move setup code into a @BeforeClass method.
  • Split one test flow into multiple TestNG @Test methods.
  • Use dependsOnMethods for dependent TestNG test execution.

Prerequisites

Before starting this tutorial, make sure you completed the previous tutorials:

  • Part 1: Create a Java Maven API testing project with JPostman.
  • Part 2: Execute REST API requests using the RestAssured adapter.
  • Part 3: Chain API requests using an access token.
  • Part 4: Protect request and response logs with jpostman-secure.

You should already have:

  • A Maven test project.
  • Exported Postman collection JSON file.
  • Exported Postman environment JSON file.
  • A secure policy file such as demo_test_rule.ini.
  • REST Assured executor configured.
  • A working TestNG test that executes a login request and an authenticated user request.
  • Basic secure logging with SecureContext.

Step 1: Update the Maven Version

Open the pom.xml file and update the JPostman version.

Example:

<jpostman.version>1.1.3</jpostman.version>

If your project is still using an older version such as 1.0.5, replace it with the new release version.

Step 2: Replace Separate Dependencies with jpostman-testng

In earlier tutorials, the project used separate dependencies for TestNG, jpostman-core, and jpostman-secure.

With the native TestNG module, you can simplify the dependency list and use:

<dependency>
    <groupId>io.github.jpostman</groupId>
    <artifactId>jpostman-testng</artifactId>
    <version>${jpostman.version}</version>
    <scope>test</scope>
</dependency>

The jpostman-testng module provides TestNG integration and includes the JPostman context and secure assertion flow used in this tutorial.

If the old testng.version property is no longer used in your pom.xml, you can remove it.

Step 3: Update the Maven Project

After changing the dependency, update the Maven project from your IDE.

In Eclipse:

  1. Right-click pom.xml.
  2. Go to Maven.
  3. Select Update Project.
  4. In the Update Maven Project dialog, click OK.

This downloads the latest JPostman dependency changes.

Step 4: Use secret(...) for Environment Values

Since the environment values are stored in the context as protected values, the request can be resolved by the context.

Example:

SecureContext base = SecureContext.create()
        .secret(ctx.getEnvironment())
        .load(getClass().getResourceAsStream("demo_test_rule.ini"));

Because the context now has the environment values, the code can avoid repeated request resolution calls such as:

.builder().build()

Step 5: Use Executor Support from the Secure Context

In the new release, SecureContext.from(...) can accept an ApiExecutor, not only an ApiResponse.

This means the extra .response() call can be removed when wrapping an executor.

Example:

SecureResponse resUser = base.from(
        RestAssuredExecutor
                .apply(reqUser)
                .auth()
                .oauth2(accessToken)
);

Step 6: Validate a Field Before Reading It

Before using:

resToken.path("accessToken")

it is recommended to validate that the field exists.

Example:

assertTrue(resToken.exists("accessToken"), "Access token not found");

This makes the failure easier to understand if the response does not contain the expected field.

Step 7: Add Custom Assertion Messages

In TestNG, the second assertion parameter lets you provide a custom message.

Example:

assertTrue(resToken.exists("accessToken"), "Access token not found");

Custom messages are easier to understand than default assertion output such as:

expected [true] but found [false]

Step 8: Use TestNG SoftAssert

By default, a hard assertion stops the test after the first failure.

If you want to collect multiple assertion failures in one run, use TestNG SoftAssert.

Import:

import org.testng.asserts.SoftAssert;

Example:

SoftAssert asserts = new SoftAssert();

asserts.assertEquals(resToken.statusCode(), 200, "Unexpected status code");
asserts.assertTrue(resToken.exists("accessToken"), "Access token not found");

asserts.assertAll();

The assertAll() call is required. Without it, the test may pass even when soft assertions failed.

Step 9: Cache the Access Token

After splitting the API flow into separate test methods, local variables from one method are no longer visible in another method.

A clean option is to store the token in SecureContext.cache.

Example:

base.cache("accessToken", resToken.path("accessToken"));

Then read it later:

String token = base.cache().get("accessToken").toString();

The cached value can be any object type, so calling toString() is useful when passing it to an OAuth2 token method.

Step 10: Move Shared Values to Class Variables

When test code is moved into multiple methods, shared values should be stored at the class level.

Example:

private Collection col;
private SecureContext base;

Then initialize them once in the setup method:

@BeforeClass
public void setup() throws Exception {
    Context ctx = JPostman.load(
            getClass().getResourceAsStream("DummyJSON.postman_collection.json"),
            getClass().getResourceAsStream("DummyJSON.postman_environment.json"));

    col = ctx.getCollection();

    base = SecureContext.create()
            .secret(ctx.getEnvironment())
            .load(getClass().getResourceAsStream("demo_test_rule.ini"));
}

Step 11: Add @BeforeClass

If the setup method is not annotated, TestNG will not run it before the tests.

This can cause a NullPointerException when class variables such as base or col are used before initialization.

Add:

import org.testng.annotations.BeforeClass;

Then annotate the setup method:

@BeforeClass
public void setup() throws Exception {
    // setup code
}

Step 12: Split the Flow into Separate Test Methods

Most test frameworks work better when separate scenarios are placed in separate test methods.

For this flow, the login request can be moved into a method such as:

@Test
public void getTokens() {
    // login request
}

The authenticated user request can be moved into:

@Test
public void getCurrentAuthUser() {
    // authenticated user request
}

Step 13: Use TestNG dependsOnMethods

If the second test requires the access token created by the first test, make the second test depend on the first one.

Example:

@Test(dependsOnMethods = "getTokens")
public void getCurrentAuthUser() {
    // authenticated user request
}

If getTokens fails, TestNG skips getCurrentAuthUser.

This prevents the second request from running when the token was not created successfully.

Step 14: Use the Fluent TestNG Context

Complete Example Flow

import static org.testng.Assert.assertEquals;

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.asserts.SoftAssert;

import io.jpostman.Collection;
import io.jpostman.Environment;
import io.jpostman.JPostman;
import io.jpostman.JPostman.Context;
import io.jpostman.restassured.RestAssuredExecutor;
import io.jpostman.secure.SecureContext;
import io.jpostman.secure.SecureRequest;
import io.jpostman.secure.SecureResponse;

public class DemoTest {

	private Collection col;
	private SecureContext base;
	
	@BeforeClass
	public void setup() throws Exception {
		Context cxt = JPostman.load(getClass().getResourceAsStream("DummyJSON.postman_collection.json"),                        
				getClass().getResourceAsStream("DummyJSON.postman_environment.json"));
		Environment env = cxt.getEnvironment();
		col = cxt.getCollection();
		base = SecureContext.create().secret(env)
				.load(getClass().getResourceAsStream("demo_test_rule.ini"));
	}
	
	@Test
	public void getTokens() throws Exception {
		SoftAssert asserts = new SoftAssert();
		SecureRequest reqToken = base.from(col.getRequest("Login user and get tokens"));
		SecureResponse resToken = base.from(RestAssuredExecutor.execute(reqToken));
		asserts.assertEquals(resToken.statusCode(), 200, "Body Result: "+ base.log() + "\n");
		asserts.assertTrue(resToken.exists("accessToken"), "Access token not found");
		base.cache().put("accessToken", resToken.path("accessToken"));
		asserts.assertAll();
	}
	
	@Test(dependsOnMethods = "getTokens")
	public void getCurrentAuthUser() {
		SecureContext secure = base.loadRules("user").filter("id", "firstName", "lastName", "gender");
		SecureRequest reqUser = secure.from(col.getRequest("Get current auth user"));
		SecureResponse resUser = secure.from(RestAssuredExecutor.apply(reqUser)
				.auth().oauth2(secure.cache().get("accessToken").toString()));
		assertEquals(resUser.statusCode(), 200, "Body Result: "+ secure.log() + "\n");

		resUser.print();
	}
}

Update the collection, environment, and policy file names if your files use different names.

What the Test Does

This test performs a secure authenticated API flow:

  1. Loads the Postman collection and environment.
  2. Stores environment values as protected values with secret(...).
  3. Loads reusable masking rules from demo_test_rule.ini.
  4. Executes the login request when an access token is needed.
  5. Validates that the login response contains accessToken.
  6. Verifies that the login request returns status code 200.
  7. Caches the access token for reuse.
  8. Applies the [user] secure policy profile.
  9. Filters the authenticated user response output.
  10. Executes the authenticated user request with OAuth2 authorization.
  11. Verifies the authenticated request response.

Why This Is Useful

Native TestNG support helps reduce repetitive code in Java API tests.

The fluent context style makes it easier to:

  • Reuse exported Postman requests.
  • Resolve environment values from the secure context.
  • Execute requests using the current context.
  • Validate required response fields before reading them.
  • Cache values across related tests.
  • Keep secure request and response logs available for failure output.
  • Apply policy rules per API flow.

Summary

In this tutorial, we updated the project to use JPostman release 1.1.3, replaced separate dependencies with jpostman-testng, moved shared setup into @BeforeClass, used SoftAssert, cached an access token, and reviewed the new fluent TestNG context syntax.

We also reviewed how TestNG dependsOnMethods can stop dependent tests when a required setup test fails.

In the next tutorial, we will review the JPostman style for writing tests across different frameworks with only minor changes.

Related Links

Clone this wiki locally