---
title: "Testing"
description: "@codespar/sdk/testing: fakeSession, a Session your tests can drive without network, keys or a tenant."
---

<VersionBadge pkg="@codespar/sdk" />

`@codespar/sdk/testing` is a separate entry point of the same package. It gives your test a `Session` that satisfies the same type the runtime returns, answers the tool calls you register, and never opens a socket.

```ts twoslash
import { fakeSession } from "@codespar/sdk/testing";

const session = fakeSession({
  codespar_charge: {
    success: true,
    data: { id: "chg_0000", pix_copy_paste: "00020126…" },
    error: null,
    duration: 0,
    server: "codespar",
    tool: "codespar_charge",
  },
});

const result = await session.execute("codespar_charge", { amount: 150 });
```

## `fakeSession`

<SdkMethod name="fakeSession" signature="fakeSession(responses?: Record<string, FakeSessionResponse>, options?: FakeSessionOptions): Session" kind="read" />

| Parameter | Type | Required | Description |
|---|---|---|---|
| `responses` | `Record<string, ToolResult \| (input) => ToolResult \| Promise<ToolResult>>` | no | One entry per tool name. A function receives the arguments the code under test passed, so you can assert on them or vary the answer. |
| `options.lenient` | `boolean` | no | When true, a tool name you did not register resolves to `{ success: true, data: {} }` instead of throwing. |

A function response is the useful half: it is where you check that the code under test sent the amount, the currency and the idempotency key you expect.

```ts twoslash
import { fakeSession } from "@codespar/sdk/testing";
// ---cut---
const calls: Array<Record<string, unknown>> = [];

const session = fakeSession({
  codespar_charge: (input) => {
    calls.push(input);
    return {
      success: true,
      data: { id: "chg_0001" },
      error: null,
      duration: 0,
      server: "codespar",
      tool: "codespar_charge",
    };
  },
});

await session.execute("codespar_charge", { amount: 150, currency: "BRL" });
calls[0].currency; // "BRL"
```

<Callout type="info">
**A `ToolResult` is the whole envelope.** `success` and `data` are not enough: the type also requires `error`, `duration`, `server` and `tool`, and `tool_call_id` and `called_at` are optional. The examples above carry the full shape, which is what the runtime returns and what your assertions will read.
</Callout>

<Callout type="warn">
**A fake is not the mocks engine.** `fakeSession` answers in your process and proves how your code reacts to a result. It does not exercise policy, approval, routing or the receipt chain. For a run that goes through the real runtime with deterministic providers, use [test mode](/docs/concepts/test-mode) with a test key: the two answer different questions, and a green fake says nothing about a policy that would have denied the payment.
</Callout>

## Strict by default

Without `lenient`, calling a tool you did not register throws. That default is deliberate: a test that silently passes on a tool nobody stubbed is a test that will keep passing after the code starts calling something else.

<NextStepsGrid items={[
  { label: "CONCEPT", title: "Test mode", description: "The runtime's own deterministic mode: real policy, real routing, no money.", href: "/docs/concepts/test-mode" },
  { label: "SDK", title: "Tools, execute and send", description: "The session methods a fake answers for.", href: "/docs/api/sdk/session" },
  { label: "SDK", title: "Errors", description: "Tool-result codes, and the helpers that read them.", href: "/docs/api/sdk/errors" },
]} />
