integration

GET a resource over HTTP

The smallest useful integration test: start the Express app, issue GET /items/1, and check status plus payload. Compare Supertest, Playwright request, and pytest + httpx.

SUT: js-api · id: integration.http-get

Cached CI results from 10/4/2026, 3:02:04 AM (ci · ff16d82)

Testing tool

Supertest · HTTP integration helper on top of a JS runner · MIT

Supertest drives an Express/Connect/Fastify app without you opening a public port. You pass the app instance; SuperTest injects requests through the middleware stack and exposes .get/.post plus status and body.

This repo runs SuperTest inside Vitest. createApp() returns Express; request(app).get(...) walks routing and JSON handlers in-process. That is still an integration test (full HTTP stack) but cheaper than bind + curl.

Testing architecture

Integration tests cross a real boundary — here, HTTP. The Express app in samples/js-api is the SUT. Variants either inject requests in-process (SuperTest) or speak TCP to an ephemeral port (Playwright request, pytest + httpx). Assertions target status codes and JSON contracts, not private helpers.

The SUT is the Express app (routing + JSON), not add() or clamp(). Each variant issues GET /items/1 through that HTTP stack and asserts 200 plus { id: "1", name: "Notebook" }. SuperTest stays in-process; Playwright and pytest bind an ephemeral port so the client is a real HTTP peer.

SUT: js-api · samples/js-api/src/app.js · run npm test in examples/integration/http-get/supertest

Code under test · samples/js-api/src/app.js
import express from "express";

/** In-memory catalog — no DB so integration tests stay local and cheap. */
const ITEMS = new Map([["1", { id: "1", name: "Notebook" }]]);

/**
 * Build the Express app (no listen). Tests import this and bind a port themselves.
 */
export function createApp() {
  const app = express();
  // Parse JSON bodies if a later POST example needs them
  app.use(express.json());
  // Baseline browser-isolation headers — asserted by security.http-headers
  app.use((_req, res, next) => {
    res.setHeader("X-Content-Type-Options", "nosniff");
    res.setHeader("X-Frame-Options", "DENY");
    next();
  });

  // Liveness probe — integration + load/microbench examples hit this
  app.get("/health", (_req, res) => {
    res.json({ ok: true });
  });

  // Read one item by id from the in-memory store
  app.get("/items/:id", (req, res) => {
    const item = ITEMS.get(req.params.id);
    if (!item) {
      // Stable error contract: same JSON shape for every missing id
      res.status(404).json({ error: "not_found", id: req.params.id });
      return;
    }
    res.json(item);
  });

  return app;
}
Test · examples/integration/http-get/supertest/items.test.js · MIT · run in examples/integration/http-get/supertest: npm test
// Vitest runner + SuperTest HTTP assertions against Express
import { describe, it, expect } from "vitest";
import request from "supertest";
// Import the app factory — SuperTest drives it in-process (no real port)
import { createApp } from "../../../../samples/js-api/src/app.js";

describe("GET /items/:id", () => {
  it("returns the known item", async () => {
    const app = createApp();
    // SuperTest issues a real HTTP request through the Express stack
    const res = await request(app).get("/items/1");
    expect(res.status).toBe(200);
    expect(res.body).toEqual({ id: "1", name: "Notebook" });
  });
});