# atom.io/testing

Source: docs/source/pages/docs/testing.mdx
URL: /docs/testing

# <low-emphasis>atom.io</low-emphasis>/testing

`atom.io/testing` provides helpers for tests that use atom.io stores.

## package contents

<table-wrapper>

| Export | Description |
| --- | --- |
| `takeSnapshot` | Capture a store and return an object that can restore it later. |
| `Snapshot` | A snapshot object with `store` and `restore()`. |
| `stateExists` | Check whether a state exists in the implicit store without creating it. |
| `stateExistsInStore` | Check whether a state exists in a specific store without creating it. |
| `storeHasStateValues` | Check whether a store currently retains any state values. |
| `hasImplicitStoreBeenCreated` | Check whether the implicit store exists without creating it. |
| `setTestLogLevel` | Set the implicit store logger level for tests and return the logger. |

</table-wrapper>

## takeSnapshot

`takeSnapshot` captures the current shape of a store and returns a `Snapshot`.

Use it when your tests declare atoms, selectors, or families at module scope and you want
each test to start from the same assembled store without rebuilding that setup by hand.

For the common case, call it once at module scope after your test states are declared,
then restore it in `afterEach`.

### take snapshot
Source: docs/source/exhibits/tooling/testing/take-snapshot.ts

```ts
import { atom, getState, selector, setState } from "atom.io"
import { takeSnapshot } from "atom.io/testing"
import { afterEach, expect, test } from "vitest"

const countAtom = atom<number>({
	key: `count`,
	default: 0,
})

const doubledSelector = selector<number>({
	key: `doubled`,
	get: ({ get }) => get(countAtom) * 2,
})

const snapshot = takeSnapshot()

afterEach(() => {
	snapshot.restore()
})

test(`doubledSelector can be tested without React`, () => {
	setState(countAtom, 3)
	expect(getState(doubledSelector)).toBe(6)
})

test(`the implicit store is reset after each test`, () => {
	expect(getState(doubledSelector)).toBe(0)
})
```

That pattern keeps the test file visually familiar: setup at the top, then a normal
`afterEach` block.

## snapshot.store

The returned `Snapshot` also exposes the snapshotted store as `snapshot.store`.

That is useful when you want to pass the captured template store to a store-specific
testing helper.

### custom store
Source: docs/source/exhibits/tooling/testing/custom-store.ts

```ts
import { atom } from "atom.io"
import { stateExistsInStore, takeSnapshot } from "atom.io/testing"

const countAtom = atom<number>({
	key: `count`,
	default: 0,
})

const snapshot = takeSnapshot()

stateExistsInStore(snapshot.store, countAtom) // -> true

snapshot.restore()
```

## stateExists

Use `stateExists` to check whether an atom, selector, or family member exists in the
implicit store without creating it.

Pass a token directly, or pass a family token and a key.

### state exists
Source: docs/source/exhibits/tooling/testing/state-exists.ts

```ts
import { atomFamily, disposeState, findState } from "atom.io"
import { stateExists } from "atom.io/testing"
import { expect, test } from "vitest"

const countAtoms = atomFamily<number, string>({
	key: `count`,
	default: 0,
})

test(`a disposed family member no longer exists`, () => {
	expect(stateExists(countAtoms, `a`)).toBe(false)

	const countA = findState(countAtoms, `a`)

	expect(stateExists(countA)).toBe(true)
	expect(stateExists(countAtoms, `a`)).toBe(true)

	disposeState(countA)

	expect(stateExists(countA)).toBe(false)
	expect(stateExists(countAtoms, `a`)).toBe(false)
})
```

Use `stateExistsInStore` when you want the same non-creating check against a specific
store, such as `snapshot.store`.

## storeHasStateValues

Use `storeHasStateValues` to check whether a store is retaining state values.

It defaults to the implicit store. Pass a store explicitly when you want to inspect a
different store.

This is most useful for tests that need to assert that a failed operation did not commit
any values.

### store has state values
Source: docs/source/exhibits/tooling/testing/store-has-state-values.ts

```ts
import { atom, runTransaction, transaction } from "atom.io"
import { storeHasStateValues } from "atom.io/testing"
import { expect, test } from "vitest"

const countAtom = atom<number>({
	key: `count`,
	default: 0,
})

const failingTransaction = transaction({
	key: `failing`,
	do: ({ set }) => {
		set(countAtom, 1)
		throw new Error(`nope`)
	},
})

test(`a failed transaction does not commit values`, () => {
	try {
		runTransaction(failingTransaction)()
	} catch {
		// expected
	}

	expect(storeHasStateValues()).toBe(false)
})
```

## setTestLogLevel

Use `setTestLogLevel(null)` in committed tests to keep atom.io logs quiet.

The helper returns the implicit store logger, so you can spy on `error`, `warn`, or
`info` without enabling console output.

### test log level
Source: docs/source/exhibits/tooling/testing/test-log-level.ts

```ts
import { atom, setState } from "atom.io"
import { setTestLogLevel } from "atom.io/testing"
import { expect, test, vitest } from "vitest"

const countAtom = atom<number>({
	key: `count`,
	default: 0,
})

test(`state updates can be asserted without printing logs`, () => {
	const logger = setTestLogLevel(null)
	const error = vitest.spyOn(logger, `error`)
	const warn = vitest.spyOn(logger, `warn`)

	setState(countAtom, 1)

	expect(error).not.toHaveBeenCalled()
	expect(warn).not.toHaveBeenCalled()
})
```

## hasImplicitStoreBeenCreated

Use `hasImplicitStoreBeenCreated` when a test needs to assert that code did not touch the
global implicit store.

It checks for the store without creating it.

### has implicit store been created
Source: docs/source/exhibits/tooling/testing/has-implicit-store-been-created.ts

```ts
import { Silo } from "atom.io"
import { hasImplicitStoreBeenCreated } from "atom.io/testing"

hasImplicitStoreBeenCreated() // -> false

new Silo({
	name: `isolated`,
	lifespan: `ephemeral`,
	isProduction: false,
})

hasImplicitStoreBeenCreated() // -> false
```
