Guides
Testing
Instant test environment reset with fork
The Problem
Traditional test environments suffer from state pollution:
- Tests affect each other through shared database state
- File system changes persist between runs
- Flaky tests due to execution ordering
The Iris Solution
Keep one seeded base sandbox alive. Each test forks from it — fully isolated, zero setup overhead per test.
import { Sandbox } from '@iris/sdk'
import { describe, it, beforeAll, afterAll, afterEach } from 'vitest'
let base: Sandbox
beforeAll(async () => {
base = await Sandbox.create()
// Install deps, seed database, etc.
await base.exec.run('npm install')
await base.exec.run('npm run db:seed')
})
afterAll(async () => {
await base.kill()
})
describe('User API', () => {
let env: Sandbox
beforeEach(async () => {
// Each test starts from the same clean state
env = await base.fork()
})
afterEach(async () => {
await env.kill()
})
it('creates a user', async () => {
const result = await env.exec.run('npm test -- user.create.test.ts')
expect(result.exit_code).toBe(0)
})
it('deletes a user', async () => {
const result = await env.exec.run('npm test -- user.delete.test.ts')
expect(result.exit_code).toBe(0)
})
})Parallel Test Execution
Fork from the same base in parallel — no interference:
const tests = [
'auth.test.ts',
'users.test.ts',
'payments.test.ts',
'notifications.test.ts',
]
const results = await Promise.all(
tests.map(async (test) => {
const env = await base.fork()
const result = await env.exec.run(`npm test -- ${test}`)
await env.kill()
return { test, passed: result.exit_code === 0, output: result.stdout }
}),
)
const failed = results.filter((r) => !r.passed)
if (failed.length > 0) {
console.error('Failed:', failed.map((r) => r.test))
}Integration Testing
Test against real services with a guaranteed-clean environment per suite:
async function integrationTest() {
const base = await Sandbox.create()
// Start services once
await base.exec.run('docker-compose up -d')
await base.exec.run('python3 wait_for_services.py')
// Each suite gets a fresh fork — database is in the same state for all
await Promise.all(
testSuites.map(async (suite) => {
const env = await base.fork()
try {
const result = await env.exec.run(`pytest ${suite}`)
if (!result.ok) console.error(suite, result.stderr)
} finally {
await env.kill()
}
}),
)
await base.kill()
}CI/CD Integration
GitHub Actions
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install SDK
run: npm install @iris/sdk
- name: Run Tests
env:
IRIS_API_KEY: ${{ secrets.IRIS_API_KEY }}
run: npm testPerformance Comparison
| Approach | Setup Time | Per-Test Overhead |
|---|---|---|
| Docker | 30-60s | 5-10s |
| VM Snapshot | 10-30s | 2-5s |
| Iris Fork | Once | <1ms |
Iris forks are 1000x faster than traditional VM snapshots because only the pages a test actually writes are copied — everything else is shared read-only with the base.