The Argument

“We already use Playwright for frontend E2E tests. Let’s reuse it for backend API testing too, so we don’t introduce another tool.”

This sounds pragmatic. It is not. It confuses tool consolidation with engineering discipline.

Context

  • Stack: React frontend + Spring Boot backend.
  • Repos: Separate repositories for frontend and backend.
  • Existing coverage: @SpringBootTest + MockMvc for component/integration tests (already done).
  • The question: For black-box API tests against a running backend instance, should we use Playwright’s APIRequestContext or a dedicated API testing tool?

The “Reuse” Fallacy

The argument for Playwright rests on reuse: “We already have it, so using it again is free.”

This only holds when the frontend E2E tests and backend API tests share the same repo, same CI pipeline, same test suite, and same runtime. Our frontend and backend are in separate repositories.

Using Playwright in the backend repo means:

What you think you’re gettingWhat you’re actually getting
Reuse of an existing toolA second, independent installation of Playwright
Shared test infrastructureTwo separate setups that share nothing
FamiliarityBackend developers writing TypeScript tests for a Java application

What It Actually Costs

1. A Foreign Ecosystem in the Backend Repo

The backend is a Spring Boot / Gradle project. Introducing Playwright means:

  • package.json in a Java repo
  • node_modules/ alongside src/main/java/
  • TypeScript test files alongside JUnit tests
  • npm as a second dependency manager alongside Gradle
  • Node.js in every developer’s local environment and CI

This is dual-stack maintenance for a single project.

2. Assertions Designed for UI, Not APIs

Compare how API assertions look in practice:

Playwright (TypeScript):

const response = await request.get('/items?id=1,2');
expect(response.status()).toBe(207);
const body = await response.json();
expect(body.results).toHaveLength(2);
expect(body.results[0].status).toBe(200);
expect(body.results[0].data.name).toBe('Widget A');
// No JSON schema validation without extra libraries

REST Assured (Java):

given()
    .queryParam("id", "1,2")
.when()
    .get("/items")
.then()
    .statusCode(207)
    .body("results.size()", equalTo(2))
    .body("results[0].status", equalTo(200))
    .body("results[0].data.name", equalTo("Widget A"))
    .body(matchesJsonSchemaInClasspath("items-response-schema.json"));

Karate (DSL):

Given url baseUrl + '/items'
And param id = '1,2'
When method get
Then status 207
And match response.results == '#[2]'
And match response.results[0].status == 200
And match response.results[0].data.name == 'Widget A'

Hurl (plain text):

GET {{base_url}}/items?id=1,2
HTTP 207
[Asserts]
jsonpath "$.results" count == 2
jsonpath "$.results[0].status" == 200
jsonpath "$.results[0].data.name" == "Widget A"

REST Assured, Karate, and Hurl were built for this. Playwright was not. The difference compounds as the test suite grows.

When Tests Go Beyond HTTP

Our black-box tests also:

  • Query the database to verify state changes
  • Check Redis keys for cache population/invalidation
  • Verify WireMock stub invocations for downstream API calls
VerificationAvailable in Java?Available in Node.js?
Database stateJDBC / JPA (already in project)Requires pg/mysql2
Redis keysJedis / Lettuce (already in project)Requires ioredis
WireMock stubsWireMock Java API (already in project)HTTP API only
Message queuesSpring AMQP / Kafka client (already in project)Requires amqplib/kafkajs

Using Playwright means maintaining a second set of infrastructure clients in a second language for the same databases and services your Java backend already connects to.

The AI-Assisted Development Reality

“But Java is verbose and slow to write.”

This was true three years ago. AI writes the test code now. Whether the test is in Java or TypeScript, an AI assistant generates it in seconds.

What AI does not eliminate:

ConcernAI helps?Still matters?
Writing test codeYesNo longer the bottleneck
Java boilerplateYesNo longer the bottleneck
Node.js runtime in Java CI pipelineNoYes
Second set of infrastructure clientsNoYes
Debugging Node.js stack traces in a Java projectNoYes
Unified test reporting (JUnit/Surefire)NoYes

AI eliminates the authoring argument. It does not eliminate the operational argument.

Right Tool for the Job

Test typeRecommended toolWhy
Pure HTTP smoke testsHurl or KarateScripting feel, lightweight, no compilation
Integration verification (DB/Redis/WireMock)REST Assured + JUnitSame language, same clients, same pipeline
Frontend E2E with API setupPlaywrightRight tool in the frontend repo

When Playwright API Testing IS Valid

ScenarioWhy Playwright fits
API setup/teardown within E2E testsShare auth cookies between browser and API calls
Frontend-perspective contract checksTest that the API returns what the React app expects
Same-repo monolithOne test runner, one CI step
Quick smoke tests in E2E suite“Is the login endpoint up?” before browser tests

None of these apply to “comprehensive black-box API testing of a Spring Boot backend in a separate repo.”

Comparison Table

DimensionPlaywrightREST AssuredKarateHurl
LanguageTypeScriptJavaKarate DSL (JVM)Plain text
Native to Spring Boot repo?NoYesYesYes
JSON Schema validationRequires AjvBuilt-inBuilt-inNo
CI overhead in Java repoNode.js + npm installZeroZeroSingle binary
DB/Redis/WireMock accessSeparate Node.js clientsExisting Java clientsJava interopNo
Purpose-built for API testingNo (side feature)YesYesYes

The Bottom Line

Three arguments were made for Playwright. All three collapse:

  1. “Reuse” – separate repos mean no reuse. You’re independently installing a Node.js tool in a Java project.
  2. “Lightweight mode” – skipping browser downloads removes binary bloat, but not the ecosystem mismatch.
  3. “Java is too verbose” – AI generates REST Assured tests as easily as Playwright tests. The authoring cost is equal. The operational cost is not.

Use the right tool for the job, not the same tool for every job.