Authors REST Assured (Java) API tests using the given().when().then() BDD-style DSL - status code + JSON/XML path assertions + authentication (Basic, OAuth2, API key). Configures Maven / Gradle dependencies, runs via JUnit 5, and emits Surefire / JaCoCo reports for CI gating. Use when the project is on the JVM and wants type-safe API tests in the app's own language; for a Gherkin feature-file flow on the same JVM use karate-testing, for YAML tests on the pytest stack use tavern-testing.
88
90%
Does it follow best practices?
Impact
88%
1.10xAverage score across 10 eval scenarios
Passed
No findings from the security scan
We moved the billing API behind a new gateway last Thursday. Since then every
test in InvoiceApiIT fails with 401, while the same credentials work fine
from curl and from the mobile client. Nothing in the test module changed in
that release.
The gateway team sent us this from their access log for one test run. Note that there are two entries for each single call our test makes:
10.4.2.19 "GET /v1/invoices/INV-2200" 401 0.004s auth=absent
10.4.2.19 "GET /v1/invoices/INV-2200" 401 0.004s auth=absentThey also pointed out that unlike the old server, their gateway answers an
unauthenticated request with a bare 401 and does not send back a
WWW-Authenticate header, because the platform team turned that off as part of
a fingerprinting-reduction change and will not turn it back on.
Separately, our SRE has been asking us to stop doubling the request count on the billing service during the nightly run - our suite shows up as roughly twice its real size in their dashboards, and it has been that way for as long as anyone remembers.
Out of scope: do not change any assertion or endpoint, do not add or remove
test cases, and do not touch pom.xml.
Extract the following files before beginning.
=============== FILE: src/test/java/com/example/billing/InvoiceApiIT.java =============== package com.example.billing;
import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize;
class InvoiceApiIT {
private static String user; private static String password;
@BeforeAll static void setup() { RestAssured.baseURI = System.getProperty("api.baseURI", "http://localhost:8080"); user = System.getenv("BILLING_SVC_USER"); password = System.getenv("BILLING_SVC_PASSWORD"); }
@Test @DisplayName("an issued invoice can be fetched by id") void fetchesInvoice() { given(). auth().basic(user, password). accept(ContentType.JSON). when(). get("/v1/invoices/INV-2200"). then(). statusCode(200). body("invoice_id", equalTo("INV-2200")). body("state", equalTo("issued")). body("lines", hasSize(greaterThan(0))); }
@Test @DisplayName("invoices can be listed for an account") void listsInvoicesForAccount() { given(). auth().basic(user, password). accept(ContentType.JSON). queryParam("account_id", "ACC-77"). when(). get("/v1/invoices"). then(). statusCode(200). body("invoices.invoice_id", hasSize(greaterThan(0))); }
@Test @DisplayName("a draft invoice can be voided") void voidsDraftInvoice() { given(). auth().basic(user, password). accept(ContentType.JSON). contentType(ContentType.JSON). body("{"reason":"duplicate"}"). when(). post("/v1/invoices/INV-2201/void"). then(). statusCode(200). body("state", equalTo("void")); }
@Test @DisplayName("the health endpoint is open to anonymous callers") void healthIsAnonymous() { given(). accept(ContentType.JSON). when(). get("/health"). then(). statusCode(200). body("status", equalTo("ok")); } }