# Testing (/docs/composer/testing)

> For the complete Prisma documentation index, see [llms.txt](/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

Test composed services by deciding what service.load() returns, with mockService for unit tests and bootstrapService for integration tests.

Location: Composer > Testing

Your app's code gets every dependency from one call, `service.load()`, and nothing from arguments or globals. That makes testing a matter of deciding what `load()` returns; the code under test is never modified. There are two tools, and you pick by how much of the real path you want to exercise:

| You want to...                                               | Use                | From                                    |
| ------------------------------------------------------------ | ------------------ | --------------------------------------- |
| Call a page, action, or handler directly, dependencies faked | `mockService`      | `@prisma/composer/testing`              |
| Boot the real built entry and drive it over real HTTP        | `bootstrapService` | `@prisma/composer-prisma-cloud/testing` |

Most tests are the first kind. Reach for the second when the thing you are proving is the wiring itself: that the service boots, reads its config, builds its clients, and answers requests.

## Unit tests: `mockService`

`mockService(service, overrides)` returns a copy of the service whose `load()` yields your fakes and whose `input()` yields the object you pass under the reserved `input` key. Everything goes in one flat object: dependency names route to `load()`, and `input` routes to `input()` (required exactly when the service declares an input schema; the input double is handed over as-is, not validated). The fakes are type-checked against the service's declared dependencies, so a double with the wrong shape does not compile.

Substituting the mocked service for the real one is your test runner's job: `vi.mock` in Vitest, `mock.module` in `bun test`. A Vitest example, testing a Next.js page:

```tsx title="app/page.test.tsx"
import { renderToString } from 'react-dom/server';
import { mockService } from '@prisma/composer/testing';
import realService from '../src/service.ts';

vi.mock('../src/service.ts', () => ({
  default: mockService(realService, {
    auth: { verify: async () => ({ ok: true }) }, // wrong shape = compile error
  }),
}));

import Page from './page.tsx';

it('renders the verified state', async () => {
  expect(renderToString(await Page())).toContain('Signed in: true');
});
```

No server, no database, no environment; the page renders against the fake.

## Integration tests: `bootstrapService`

`bootstrapService` boots the service's real built entry in-process, fed the same way a deployed boot is fed, except you choose the values. Point a dependency at a stand-in you run on a loopback port, then make real HTTP requests. Run these under `bun test`:

```ts title="service.integration.test.ts"
import { bootstrapService } from '@prisma/composer-prisma-cloud/testing';
import fakeAuth from '@my-app/auth/fake'; // an in-memory handler, no db
import storefront from '../src/service.ts';

const fake = Bun.serve({ port: 0, fetch: fakeAuth });

const app = await bootstrapService(storefront, {
  service: { port: 4310 },
  inputs: { auth: { url: fake.url.href } },
});

const res = await app.fetch(new Request(app.url));
expect(await res.text()).toContain('Signed in: true');
```

Five things to know:

- **Build first.** It boots the _built_ entry, so the test task must depend on the build.
- **Pass a concrete `service.port`.** The entry listens itself; no OS-assigned port is reported back.
- **There is no `close()`.** Run each integration-test file in its own process (`bun test` does this per file) and the server dies with it.
- **The service's code is untouched.** If you find yourself editing `server.ts` to make it testable, something upstream is wrong.
- **You do not need a service key.** Only a deploy (or `dev`) provisions keys; in a test nothing checks, every call reaches your handler, and there is nothing to put in `inputs`.

A service with an input schema takes `input` in the config, a binding exactly like `provision()`'s, run through the real serialize and read path, so `input()` in the booted entry sees what a deploy would produce.

**Next.js services need a third argument**, a boot function, because the built entry lives inside Next's standalone output. Resolve it with `standaloneServerPath`; `bootstrapService` exports the resolved port as `process.env.PORT` before booting, which is exactly what Next's standalone server binds:

```ts
import { pathToFileURL } from 'node:url';
import { standaloneServerPath } from '@prisma/composer/nextjs/control';

await bootstrapService(storefront, config, async () => {
  await import(pathToFileURL(standaloneServerPath(storefront.build)).href);
});
```

The complete working version is [`examples/storefront-auth`](https://github.com/prisma/composer/tree/main/examples/storefront-auth) in the Composer repository.

## Writing good fakes

A dependency's type _is_ its contract, so anything of that shape is a valid fake, and the compiler holds it to that. In increasing order of realism:

- **A bare object**: `{ verify: async () => ({ ok: true }) }`. Right for most unit tests.
- **The real client over an in-memory handler**: exercises JSON encoding and schema validation, still no socket.
- **A real local server**: the fake served over actual HTTP, which is what `bootstrapService` drives.

One habit pays for all of this. Ship each service's fake from its own package as a `/fake` entry point, outside `src/`, so it cannot reach production code. Fake and service then share one contract; when the contract changes, both stop compiling at once, and every consumer's tests find out immediately.

## Next steps

- [Services and contracts](/guides/concepts-services-and-contracts): what the contract types on each end.
- [Local development](/guides/workflows-local-development): run the real app instead of booting one service.

## Related pages

- [`Apps and Modules`](/guides/concepts-apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together.
- [`Building blocks`](/guides/concepts-building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself.
- [`Core concepts`](/guides/introduction-3-core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model.
- [`Databases`](/guides/concepts-databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations.
- [`Deploying`](/guides/workflows-deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely.

## Related pages

- [Authentication & Tools](./authentication-tools-index.md)
- [Build](./build-index.md)
- [Changelog](../changelog.md)
- [Concepts](./concepts-index.md)
- [Console commands](./console-commands-index.md)
- [Contract Authoring](./contract-authoring-index.md)
- [Core Concepts](./core-concepts-index.md)
- [Data Modeling](./data-modeling-index.md)
- [Database](./database-index.md)
- [DB commands](./db-commands-index.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
