---
title: Testing
description: Test functions and in-process routes, then verify production transport behavior against a running backend.
pagination: false
sidebar:
  icon: flask-conical
---

Choose the test boundary that proves the behavior: a function, an application, or a running HTTP server. Function tests exercise one descriptor without a server.
Application tests load checked configuration and call an in-process HTTP client. Neither layer needs remote
credentials when provider-backed capabilities receive explicit fakes. Use a running backend for final
HTTP middleware, origin, cookie, and transport behavior.

## Install the selected test tooling

The minimal starter already has development-only `@relkit/testing`. In another existing app, install
it explicitly before copying these tests:

{/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

From this guide's application/package root, install only its selected requirements:

```sh
bun add --dev @relkit/testing@0.8.2
```

Keep these owner-specific versions and the lockfile together. Runtime SDKs belong in `dependencies`; build/type tooling belongs in `devDependencies`. Installing an adapter does not supply its optional SDK peers or enable the feature's configuration.

The generic harness installs no optional database, authentication or model SDK. Keep feature adapters
and native SDKs in the application's selected runtime dependencies. Use `@relkit/testing/drizzle`
for database tests, `@relkit/testing/better-auth` for database-backed auth, and native agent helpers
from `@relkit/langchain` or `@relkit/langchain/testing` after [agent setup](/guides/ai/build-an-offline-agent).

## Test one function

Use `invokeFunction` for input and output validation, declared errors, nested invocation, and handler behavior:

```ts title="templates/default/v1/api/tests/unit/orders.function.test.ts"
/** Checks orders.function through the supported public RELKIT APIs. */
import { expect, test } from "bun:test";
import { invokeFunction } from "@relkit/testing";
import orders from "@app/services/orders/service.js";

test("createOrder invokes pricing", async () => {
  const result = await invokeFunction(orders.createOrder, {
    orderId: "order-1",
    sku: "book",
    quantity: 3,
  });

  expect(result).toEqual({ orderId: "order-1", sku: "book", totalCents: 300 });
});
```

```sh title="Terminal — relkit-orders"
bun test tests/unit/orders.function.test.ts
```

Pass test clients when the function declares cache, bucket, job, or event capabilities. Agent tests should
declare or inject a deterministic native LangChain model and inspect recorded effects instead of sleeping or
calling a live service.

## Test the application boundary

Use `createTestApplication` for discovered routes, mapped input, function invocation, and provider
wiring. The [Orders tutorial test](/docs/learn/test-orders) also migrates isolated SQLite and checks
two users' ownership boundary. The starter's smallest HTTP test is:

```ts title="templates/default/v1/api/tests/integration/orders.route.test.ts"
import { afterAll, expect, test } from "bun:test";
import { createTestApplication } from "@relkit/testing";
import config from "../../relkit.config.js";

const testApp = await createTestApplication(config, { resourceProviders: "fake" });

test("POST /orders", async () => {
  const response = await testApp.http.request("/orders", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ orderId: "order-1", sku: "book", quantity: 3 }),
  });
  expect(response.status).toBe(201);
  expect(await response.json()).toEqual({
    orderId: "order-1",
    sku: "book",
    totalCents: 300,
  });
});

afterAll(() => testApp.close());
```

The harness runs in process and exposes `application.http`. It maps invalid function input to a
validation `422`, but it does not implement the entire production HTTP pipeline. It does not prove
all middleware, rate limits, OpenAPI behavior, or RPC transport. Close it in `afterAll` so runtime
state and handles do not leak into another test, then send representative requests to the running
backend as the Start journey does.

## Replace provider profiles explicitly

The commerce suite shows the complete shape for cache, bucket, realtime, and agent-state profiles alongside
native model execution:

[Complete commerce HTTP test](https://github.com/rel-kit/relkit/blob/main/examples/commerce/tests/http.test.ts)

Every graph-required capability and profile must have a replacement. `RELKIT_ENV=test` does not silently select
fakes. Use `createTestCacheFake` and `createTestBucketFake` for deterministic resource behavior, and dedicated
test event, job, or agent utilities for those contracts.

## Run the application gate

```sh title="Terminal — relkit-orders"
bun run test
bun run check
bun run typecheck
bun run build
```

Keep live integration and cloud tests separate and opt-in. In the RELKIT repository, `bun run prepush`
runs the focused merge-blocking checks without Docker; `bun run prepush:full` includes Docker acceptance.

Task-first jobs add focused unit, type, contract, restart, client, Inspector, Docker, and provider suites.
The Docker/provider commands are explicit gates; managed cloud acceptance remains separately authorized.

{/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

**Reference and source**

- [`@relkit/testing` API](/docs/api/testing)

- [`templates/default/v1/api/tests/integration/orders.route.test.ts`](https://github.com/rel-kit/relkit/blob/main/templates/default/v1/api/tests/integration/orders.route.test.ts)
