---
id: "packages/active-state/testing"
title: "Testing"
description: "Test a model without React - capture errors, record what was published, and drive operations step by step."
url: "/packages/active-state/testing"
locale: "en"
revision: "54eb34d14f2ba882"
---

# Testing

`@workspace/active-state/testing` holds the seams for tests. It is internal and unstable: use it in
tests and diagnostics, never in production code.

```ts
import {Operation, Scope, Store} from '@workspace/active-state';
import {captureErrors, controlledRun} from '@workspace/active-state/testing';
import {expect, it} from 'vitest';

it('a superseded search cannot commit', async () => {
  const errors = captureErrors();
  const scope = new Scope();
  const results = new Store<string[]>([]);
  // Each call waits until the test resolves it by its id.
  const control = controlledRun<string, string[]>();
  const search = new Operation<string, string[]>({
    scope,
    policy: 'latest-wins',
    run: async (query, ctx) => {
      const found = await control.run(query, ctx);
      ctx.commit(() => results.setState(found));
      return found;
    },
  });

  const first = search.invoke('a');
  const second = search.invoke('ab');
  control.resolve(second.id, ['Abidjan']);
  expect((await second.outcome).status).toBe('succeeded');
  expect((await first.outcome).status).toBe('superseded');
  expect(results.state).toEqual(['Abidjan']);

  scope.dispose();
  errors.release(); // throws when an error was captured but never taken
});
```

| Seam | Use |
| --- | --- |
| `captureErrors()` | capture what goes to the error sink; `take()` asserts, `release()` restores |
| `record()` | record the instrumentation events of a scenario |
| `controlledRun()` | an operation `run` whose calls you resolve or reject by id |
| `inspectScope`, `inspectStore`, and more | plain descriptions of lifetimes and subscriptions |

The complete list is in the [API reference](/packages/active-state/api#testing).
