Skip to Content

E2E Test Generation

Configure e2e in nestia.config.ts and npx nestia e2e writes a starter end-to-end test for every @TypedRoute-decorated method in your project. Each test:

  • Picks a random valid input from the route’s DTO type (via typia.random<T>()).
  • Calls the generated SDK function (no fetch strings).
  • Asserts that the response matches the declared return type.

That’s the schema-contract net: every route gets covered, on every CI run, with zero hand-written boilerplate. Your own business-logic tests live alongside them.

New to this feature? Read Tutorial β†’ Auto E2E Tests for the friendly walkthrough. This page is the reference.


Enable

nestia.config.ts
import { INestiaConfig } from "@nestia/sdk"; import { NestFactory } from "@nestjs/core"; import { AppModule } from "./src/AppModule"; const config: INestiaConfig = { input: () => NestFactory.create(AppModule), output: "src/api", e2e: "test", // output directory for the generated suite }; export default config;

Run:

Terminal
npx nestia e2e

Output: test/features/api/<controller-path>/test_api_<route>.ts (one file per endpoint).


Anatomy of a generated test

test/features/api/articles/test_api_articles_create.ts (generated)
import typia, { Primitive } from "typia"; import api from "@my-app/api"; import type { IArticle } from "@my-app/api/lib/structures/IArticle"; export const test_api_articles_create = async ( connection: api.IConnection, ): Promise<void> => { const output: IArticle = await api.functional.articles.create( connection, typia.random<Primitive<IArticle.ICreate>>(), ); typia.assert(output); };

Three lines that matter:

  1. typia.random<Primitive<IArticle.ICreate>>() β€” synthesizes a schema-valid request body.
  2. api.functional.articles.create(...) β€” the generated SDK call against connection.
  3. typia.assert(output) β€” verifies the response matches IArticle.

If the schema drifts (server returns a different shape), the assert fails on the next CI run.

Re-running npx nestia e2e overwrites these files. Keep them auto-generated and add your hand-written scenarios in sibling files (the runner picks up anything matching test_*).


Run the suite

@nestia/e2e’s DynamicExecutor walks the test directory, finds every test_* function, and runs them in series. A typical runner:

test/index.ts
import { DynamicExecutor } from "@nestia/e2e"; import { NestFactory } from "@nestjs/core"; import { AppModule } from "../src/AppModule"; const main = async () => { const app = await NestFactory.create(AppModule); await app.listen(0); const port = (app.getHttpServer().address() as any).port; const report = await DynamicExecutor.validate({ prefix: "test_", parameters: () => [{ host: `http://localhost:${port}` }], })(__dirname + "/features"); console.log(report); await app.close(); }; main().catch(console.error);

Run with npx ttsx test/index.ts. Exit code reflects pass/fail; plug into CI.


Mixing generated tests with hand-written ones

The convention is one directory per controller / domain, with both auto and hand-written files:

test/features/api/articles/ β”œβ”€β”€ test_api_articles_create.ts # generated β€” schema contract β”œβ”€β”€ test_api_articles_list.ts # generated β€” schema contract β”œβ”€β”€ test_api_articles_admin_sees_all.ts # hand-written β€” business rule └── test_api_articles_archive_idempotent.ts # hand-written β€” business rule

DynamicExecutor finds them all by the test_ prefix. The auto-generated tests prove the schema works; your hand-written tests prove the rules work.


Re-using the SDK for benchmarks

The same test files drive @nestia/benchmark β€” point it at the same directory and you get a load report:

benchmark/index.ts
import { NestiaBenchmarker } from "@nestia/benchmark"; const report = await NestiaBenchmarker.benchmark({ prefix: "test_", parameters: () => [{ host: `http://localhost:${port}` }], threads: 16, duration: 30_000, })(__dirname + "/../test/features");

You get RPS, latency percentiles, and per-endpoint error rates as a markdown report. See E2E β†’ Benchmark for the full configuration.


See also

Last updated on