agentsclimarketplace

Rest assured api tests

Skill vasyafomiuk/skills/java-playwright-e2e/skills/rest-assured-api-tests

Claude Code skill plugin marketplace — java-playwright-e2e: Java + Playwright + Cucumber (BDD) end-to-end test automation

Install
npx -y skills add vasyafomiuk/skills --skill rest-assured-api-tests

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Writes API-level and hybrid Cucumber scenarios with REST Assured, and mocks third-party dependencies with Playwright page.route. Use this skill when the user wants to: write an API-level Cucumber scenario (responses stored in ScenarioContext), call a REST endpoint from a step, build a request with an auth header and a JSON body from a fixture, store and assert a Response, check a status code or response body, validate the exact error message from an acceptance criterion, normalize dates before comparing, assert the input and API legs of three-way validation, cover a "UI-only" criterion at the API level, or stub/abort/modify an unstable external dependency so a UI test is deterministic. Project-agnostic — no application-specific assumptions.

SKILL.md

11.6 KB, as published. Nobody here has run it

REST Assured API Tests & page.route Mocking

This skill owns the API leg of the AC-to-passing-test workflow: turning an acceptance criterion into a REST Assured request, storing the Response in the shared ScenarioContext, and asserting status, body, and exact error messages. It also owns mocking unstable third-party dependencies with Playwright page.route so UI scenarios stay deterministic. You are invoked from cucumber-step-definitions; for cross-cutting rules (locator priority, web-first assertions, DI, isolation, naming) follow the orchestrator (java-playwright-e2e:orchestrator) — this skill states only the API/mocking how-to.

WHEN TO USE

Use this skill when the user wants to:

  • Write an API or hybrid scenario, or add the API assertions to an E2E flow.
  • Build a request: auth header, content type, body loaded from a JSON fixture.
  • Store a Response in ScenarioContext and assert status code / body fields.
  • Validate the exact error message an AC specifies (not a substring guess).
  • Normalize timestamps before comparing so timezone/format drift can't flake.
  • Do three-way validation — input, API response, and DB all agree.
  • Cover a criterion the UI renders by asserting the API that feeds it.
  • Mock, modify, abort, or capture a third-party call with page.route.

PREREQUISITES (and where they come from)

This skill has no fallback chain for missing inputs — point the user at the sibling that produces each one:

  • Endpoints / request + response schema, base URL, auth scheme → ask the user, or e2e-framework-setup for config/secrets wiring.
  • An existing *.feature with @api scenarios → create-test-scenarios.
  • The DB query for the third leg of three-way validation → database-validation.
  • Step glue, DI, ScenarioContext, hooks → cucumber-step-definitions.

STEP 1 — Build the request (auth, content type, body from fixture)

Keep one REST Assured wrapper in the framework api/ package. Read base URL and token from Config (never hardcode; see the orchestrator), and load bodies from versioned fixtures, not inline strings, so Gherkin stays declarative.

/** Wraps REST Assured so steps stay thin and auth/base-url live in one place. */
public class ApiClient {
    public Response post(String endpoint, String jsonBody) {
        return RestAssured.given()
                .baseUri(Config.apiBaseUrl())
                .header("Authorization", "Bearer " + Config.token())
                .contentType(ContentType.JSON)
                .body(jsonBody)
            .when().post(endpoint);
    }
}

/** Reads src/test/resources/fixtures/<name>.json from the classpath. */
public final class Fixtures {
    public static String fixture(String name) {
        try (var in = Fixtures.class.getResourceAsStream("/fixtures/" + name + ".json")) {
            return new String(Objects.requireNonNull(in, name).readAllBytes(), UTF_8);
        } catch (IOException e) { throw new UncheckedIOException(e); }
    }
}

STEP 2 — Store the Response in ScenarioContext

Never assert in the same step that fires the call. Store the Response once so later Then steps assert against it (and so the API leg of three-way validation can reuse it). ScenarioContext is injected per scenario — see cucumber-step-definitions.

public class ApiSteps {
    private final ApiClient api;
    private final ScenarioContext context;   // injected per scenario

    public ApiSteps(ApiClient api, ScenarioContext context) {
        this.api = api;
        this.context = context;
    }

    @When("User POSTs {string} with body {string}")
    public void userPosts(String endpoint, String fixtureName) {
        context.setResponse(api.post(endpoint, Fixtures.fixture(fixtureName)));
    }
}
@api @AC1
Scenario: AC1 — Creating a note returns 201 with the saved note
  When User POSTs "/api/notes" with body "validNote"
  Then API status code is 201
  And API field "title" equals "Quarterly review"

STEP 3 — Assert status, then body fields

Assert the status first (a 500 makes body assertions meaningless), then specific fields. Extract typed values with REST Assured's jsonPath(); assert real values against the source of truth, never types ("is defined", instanceof).

@Then("API status code is {int}")
public void statusIs(int expected) {
    assertEquals(expected, context.getResponse().statusCode());
}

@Then("API field {string} equals {string}")
public void fieldEquals(String path, String expected) {
    assertEquals(expected, context.getResponse().jsonPath().getString(path));
}

For richer checks deserialize into a record and assert the object, not a long list of path strings:

NoteResponse note = context.getResponse().as(NoteResponse.class);
assertEquals("Quarterly review", note.title());
assertEquals(currentUser, note.createdBy());

STEP 4 — Validate the EXACT error message from the AC

When an AC names an error message, assert it verbatim — exact text and exact status. A substring or "contains" check lets a reworded or wrong-field error pass.

@api @negative @AC2
Scenario: AC2 — Empty title is rejected with the documented message
  When User POSTs "/api/notes" with body "emptyTitle"
  Then API status code is 422
  And API error message is "Title is required."
@Then("API error message is {string}")
public void errorMessageIs(String expected) {
    assertEquals(expected, context.getResponse().jsonPath().getString("message"));
}

Drive variants with a Scenario Outline so each invalid input maps to its exact documented error. Add negatives beyond the AC to hunt bugs: empty/null, boundaries, special chars, injection strings, unauthorized (401/403), wrong content type. Never weaken an assertion to make one pass — a real failure is a bug signal (rule owned by the orchestrator).

STEP 5 — Normalize dates before comparing

Timezone and format drift between the request, the API, and the DB is a top cause of flaky API tests. Parse both sides to a common Instant/LocalDate and compare those — never compare raw timestamp strings.

/** Compares two ISO-8601 timestamps as instants, ignoring format/zone noise. */
private static void assertSameInstant(String expected, String actual) {
    assertEquals(Instant.parse(expected), Instant.parse(actual));
}

// Date-only fields: truncate before comparing.
assertEquals(LocalDate.parse("2026-06-29"),
             OffsetDateTime.parse(note.createdAt()).toLocalDate());

When seeding data for a scenario, use past dates so "date cannot precede last status" style server validation doesn't reject the setup.

STEP 6 — Three-way validation (input → API → DB agree)

For any create/update, verify all three legs agree. You own the input and API legs; defer the DB query to database-validation and assert against what its repository returns. Three pairwise asserts catch a leg that silently disagrees (e.g. the API echoes the input but the DB never persisted it).

// input  : what the test sent
// api    : context.getResponse().as(NoteResponse.class)
// saved  : repository result from the database-validation skill
assertEquals(input.status(), api.status());          // API reflects what we sent
assertEquals(api.status(),   saved.getStatus());      // API reflects the DB
assertEquals(saved.getStatus(), input.status());      // DB persisted what we sent
@api @e2e @AC3
Scenario: AC3 — Approving a record agrees across input, API, and DB
  When User PATCHes "/api/records/42" with body "approve"
  Then API status code is 200
  And API field "status" equals "APPROVED"
  And record 42 in the database has status "APPROVED"

STEP 7 — Cover every AC at the API level, even "UI-only" ones

The UI renders data the API returns, so a "the screen shows X" criterion is also checkable at the API: assert the value in the response that the UI binds to. This gives a fast, stable check that complements the UI assertion and pins down which layer broke when something fails. Pair the UI assertion (owned by playwright-page-objects / cucumber-step-definitions) with an @api assertion of the same field.

STEP 8 — Mock third-party / unstable dependencies with page.route

Don't test systems you don't control. For UI scenarios that hit a flaky or external service, intercept the call with Playwright page.route so the run is deterministic. The handler can fulfill (stub), modify (rewrite upstream), abort (simulate failure), or capture (assert the request was made). Register routes before the navigation that triggers them; the page is injected per scenario — see cucumber-step-definitions.

// FULFILL — return a canned body, never hitting the real service
page.route("**/api/third-party/**", route ->
    route.fulfill(new Route.FulfillOptions()
        .setStatus(200)
        .setContentType("application/json")
        .setBody(Fixtures.fixture("thirdPartyOk"))));

// ABORT — simulate an outage to test the UI's error state
page.route("**/api/third-party/**", route -> route.abort());

// MODIFY — let the call go through, then rewrite the upstream response
page.route("**/api/rates/**", route -> {
    APIResponse upstream = route.fetch();
    // setResponse seeds status + headers from upstream; setBody then replaces the body
    route.fulfill(new Route.FulfillOptions().setResponse(upstream)
        .setBody(overrideRate(upstream.text(), "1.10")));
});

// CAPTURE — assert the app actually called the dependency with the right payload
List<String> sent = new ArrayList<>();
page.route("**/api/audit/**", route -> { sent.add(route.request().postData()); route.resume(); });

Rules:

  • Glob match the URL (**/api/...); narrow enough to avoid catching unrelated calls.
  • Mock only what you don't own. Real, controlled endpoints should be tested for real.
  • Keep stub bodies in fixtures next to request fixtures, so they version together.
  • Routing is scenario-scoped; the per-scenario context teardown clears it (isolation rule owned by the orchestrator).

HANDOFF

  • database-validation — owns the DB leg of three-way validation: provides the repository/ORM query whose result you compare against the input and API legs.
  • cucumber-step-definitions — calls this skill; owns step glue, the injected ScenarioContext/page, hooks, and the service layer your assertions live in.
  • create-test-scenarios — produces the @api Gherkin scenarios you implement.
  • e2e-framework-setup — provides config/secrets (base URL, token) and the REST Assured dependency on the classpath.
  • the orchestrator (java-playwright-e2e:orchestrator) — single source of truth for shared conventions (web-first assertions, DI, isolation, test integrity, naming).

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.