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

# Keep test environments isolated

> Own creation and deletion in one awaited helper and assert native app behavior.

Give each independent test run an environment it owns. Keep the create call and deletion together so an assertion failure cannot skip cleanup. Share a prepared snapshot when tests need the same starting records, rather than sharing one mutable environment.

## Wrap the test body

This helper accepts a test function, gives it Salesforce credentials, and deletes the environment after the function settles. The final call checks a native response status.

```ts theme={null}
import { type Credentials, StateMachines } from '@usestatemachines/sdk';

const sm = new StateMachines();

export async function withSalesforce<T>(
  run: (salesforce: Credentials) => Promise<T>,
): Promise<T> {
  const env = await sm.environments.create(
    { apps: ['salesforce'] },
    { wait: false },
  );

  try {
    await sm.environments.wait(env.id, { status: 'running' });

    return await run(await env.connect('salesforce'));
  } finally {
    await env.delete();
  }
}

await withSalesforce(async (salesforce) => {
  const response = await salesforce.fetch('/services/data/v67.0/limits');

  if (!response.ok) {
    throw new Error(`Salesforce returned ${response.status}`);
  }
});
```

The `return await` inside the helper matters. It waits for the test body before entering `finally`. Returning an unawaited promise would let deletion race with the app requests.

Adapt the callback to your test runner's assertions and give the test enough time for environment startup and cleanup.

## Assert the behavior your integration needs

A successful create call verifies that the environment started. It does not verify the integration. Exercise your actual integration path, then assert the native response or query the resulting records.

Use an actor with the permissions the scenario expects. An administrator-only test cannot establish that the same operation works for a restricted actor. Read actors from the version that the environment runs.

Check `response.ok` for native requests. A Salesforce error response is not automatically thrown by `credentials.fetch()`.

## Reuse data without sharing writes

Prepare a snapshot once, wait until it is `ready`, and create a separate environment from its ID for each independent run. The snapshot stays unchanged as tests modify their copies. See [snapshot reuse](/snapshots/reuse).

Choose concurrency using the applicable `concurrentEnvironments` limit from `sm.me()`. Starting and running environments count toward that limit. A failed test must still reach its awaited deletion.

After a network interruption during a native write, do not wrap the write in a generic retry loop. Determine whether the record was created before repeating it. Management create idempotency does not extend to Salesforce writes.

## Own cleanup across CI retries

Use the CI secret store for `STATEMACHINES_API_KEY` and give the key the permissions needed for create, connect, reads, and deletion. Use one environment per independent test run. Add a run identifier to `labels` so a recovery job can find the environments created by that run.

Keep the accepted environment ID and create idempotency key with the CI job when interrupted jobs need recovery. An ordinary test failure reaches `finally`. A forced process termination may not. A later cleanup job can list environments with that run label and delete only the IDs the job owns.

The example's work must run inside a test runner timeout that leaves time for deletion. Passing an aborted test signal to deletion prevents cleanup. Report a deletion error along with the environment ID so cleanup can be retried.

A job retry must decide whether it is resuming the same operation or starting another independent test. Reuse the original create key only for the former. A new test with a new key gets separate data. Use the [recovery guide](/sdk-api/recovery) for an uncertain create response.


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