> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.docs.xano.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tests

> Write unit tests on an endpoint, function or middleware and workflow tests that span objects, then run them against a deployed backend or as part of a deploy.

Tests live beside the code they cover and ship with it. There are two kinds:

| Kind | Where | Asserts with |
| - | - | - |
| Unit test | a `tests: [...]` entry on a `query`, `defineFunction` or `middleware` | `expect.*` on the response, `resp()` |
| Workflow test | a standalone `workflowTest({ name, stack })` | `s.expect.*` statements in its stack |

`expect.*` builds an assertion record and `s.expect.*` builds a statement. They are not
interchangeable, and the types enforce it.

## A unit test

Each entry is a named set of inputs run against that object:

```ts theme={null}
import { query, input, inp, ref, s, c, expect, resp } from "@xano/sdk";

export const createNote = query({
  name: "create_note",
  verb: "POST",
  apiGroup: api,
  input: { body: input.text({ required: true }) },
  tests: [
    {
      name: "creates a note",
      input: { body: c.text("hello") }, // tagged values, not bare strings
      expect: [
        expect.to_be_defined(resp("id")),
        expect.to_equal(resp("body"), c.text("hello")), // subject first
      ],
    },
  ],
  stack: [s.db.add({ table: notes, row: { body: inp("body") }, as: "note" })],
  response: ref("note"),
});
```

Any statement can take a `mock` keyed by test name, which returns a value instead of doing its
work while that one test runs:

```ts theme={null}
s.set_var("total", c.int(0), { mock: { "creates a note": c.int(2) } })
```

## A workflow test

A workflow test calls other objects and asserts on what they bind:

```ts theme={null}
import { defineFunction, workflowTest, input, inp, ref, s, c } from "@xano/sdk";

export const addNote = defineFunction({
  name: "add_note",
  input: { body: input.text({ required: true }) },
  stack: [s.db.add({ table: notes, row: { body: inp("body") }, as: "note" })],
  response: ref("note"),
});

export const addNoteWorks = workflowTest({
  name: "add_note_works",
  stack: [
    s.function.call({ fn: addNote, input: { body: "hello" }, as: "created" }),
    s.expect.to_be_defined({ expr: ref("created.id") }),
    s.expect.to_equal({ expr: ref("created.body"), value: c.text("hello") }),
  ],
});
```

Register them with `.registerFunctions([addNote])` and `.registerWorkflowTests([addNoteWorks])`.

<Warning>
  Both kinds run against an **empty** datasource by default, so no `seed` rows exist while a test
  runs. Create what the test needs first (a function the stack calls), or `mock` the read. Setting
  `datasource: "live"` reads seed rows on an ephemeral, but the value is stored on the object:
  clear it before you promote, or the test clones your real database.
</Warning>

## Run them

Tests run against what is deployed, so deploy first:

```bash theme={null}
npm run xano:deploy   # deploy to the Xano Engine
npm run xano:test     # xanosdk test run-all: unit and workflow tests
```

```
→ Running 2 tests against your Xano Engine (1 unit, 1 workflow)

  PASS  creates a note  [unit: query POST create_note in notes]
  PASS  add_note_works  [workflow]  0.000s

✓ 2 passed, 0 failed
```

With no flag, `test` runs on the backend this project last deployed to. `--on` names another:

```bash theme={null}
npx xanosdk test list                         # what the backend carries, without running it
npx xanosdk test run "creates a note"         # one test by name
npx xanosdk test run-all --on ephemeral:pr-3  # a named ephemeral
npx xanosdk test run-all --kind workflow      # one family
```

`--on workspace` runs against your real database. That is safe only while every test keeps the
default empty datasource.

## Deploy and test in one step

`--test` runs the suite after a deploy, against what it just shipped. This is the first step of
the release flow:

```bash theme={null}
npx xanosdk deploy ./xano/index.ts --ephemeral --test
```

| Exit | Meaning |
| - | - |
| `0` | Deployed, and every test passed (or there are no tests). |
| `5` | At least one test failed. The deploy is **not** rolled back: the backend is live either way. |
| `6` | The suite could not be reached. Retry the run. |

`--json` (or a stdout that is not a terminal) prints one JSON document naming each test and its
status, so CI can report which test failed. See [CI/CD](/govern/ci-cd) and
[exit codes](/help/exit-codes-and-outcomes).

## Next

* Assertions, mocks, and what `s.api.call` can and cannot do in a workflow test:
  [object kinds reference](/reference/object-kinds).
* Keep secrets out of the repo: [Environment variables and secrets](/build/env-and-secrets).
* Ship what passed: [Releases](/ship/releases).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.