effect-testing
Use when testing Effect code including Effect.gen in tests, test layers, mocking services, and testing error scenarios. Use for writing tests for Effect applications.
What this skill does
# Effect Testing
Master testing Effect applications with test utilities, mock layers, and
patterns for testing effectful code. This skill covers unit testing, integration
testing, and testing concurrent and resource-managed code.
## Basic Effect Testing
### Testing with Effect.gen
```typescript
import { Effect } from "effect"
import { describe, it, expect } from "vitest"
describe("User Service", () => {
it("should fetch user by ID", async () => {
const program = Effect.gen(function* () {
const user = yield* fetchUser("123")
return user
})
const result = await Effect.runPromise(program.pipe(
Effect.provide(TestLayer)
))
expect(result.id).toBe("123")
expect(result.name).toBe("Alice")
})
})
```
### Testing Success and Failure
```typescript
import { Effect, Exit } from "effect"
import { describe, it, expect } from "vitest"
describe("Validation", () => {
it("should succeed with valid email", async () => {
const program = validateEmail("[email protected]")
const result = await Effect.runPromise(program)
expect(result).toBe("[email protected]")
})
it("should fail with invalid email", async () => {
const program = validateEmail("invalid")
const exit = await Effect.runPromiseExit(program)
expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isFailure(exit)) {
const error = Cause.failureOption(exit.cause)
expect(error._tag).toBe("ValidationError")
}
})
})
```
## Mock Layers for Testing
### Creating Test Layers
```typescript
import { Context, Effect, Layer } from "effect"
interface UserRepository {
findById: (id: string) => Effect.Effect<Option<User>, DbError, never>
save: (user: User) => Effect.Effect<User, DbError, never>
}
const UserRepository = Context.GenericTag<UserRepository>("UserRepository")
// In-memory test implementation
const UserRepositoryTest = Layer.succeed(
UserRepository,
{
findById: (id: string) =>
Effect.succeed(
id === "1"
? Option.some({ id: "1", name: "Alice", email: "[email protected]" })
: Option.none()
),
save: (user: User) =>
Effect.succeed(user)
}
)
// Use in tests
const testProgram = Effect.gen(function* () {
const repo = yield* UserRepository
const user = yield* repo.findById("1")
return user
}).pipe(
Effect.provide(UserRepositoryTest)
)
```
### Stateful Mock Layers
```typescript
import { Context, Effect, Layer, Ref } from "effect"
// Mock with state
const UserRepositoryStateful = Layer.effect(
UserRepository,
Effect.gen(function* () {
const storage = yield* Ref.make<Map<string, User>>(new Map([
["1", { id: "1", name: "Alice", email: "[email protected]" }]
]))
return {
findById: (id: string) =>
storage.get.pipe(
Effect.map((map) => {
const user = map.get(id)
return user ? Option.some(user) : Option.none()
})
),
save: (user: User) =>
storage.update((map) => map.set(user.id, user)).pipe(
Effect.map(() => user)
)
}
})
)
// Test with state
describe("User Repository", () => {
it("should save and retrieve user", async () => {
const program = Effect.gen(function* () {
const repo = yield* UserRepository
const newUser = { id: "2", name: "Bob", email: "[email protected]" }
yield* repo.save(newUser)
const retrieved = yield* repo.findById("2")
return retrieved
}).pipe(
Effect.provide(UserRepositoryStateful)
)
const result = await Effect.runPromise(program)
expect(Option.isSome(result)).toBe(true)
if (Option.isSome(result)) {
expect(result.value.name).toBe("Bob")
}
})
})
```
## Spy Layers
### Recording Calls
```typescript
import { Context, Effect, Layer, Ref } from "effect"
interface LoggerCalls {
info: string[]
error: string[]
}
const LoggerSpy = Layer.effect(
Logger,
Effect.gen(function* () {
const calls = yield* Ref.make<LoggerCalls>({
info: [],
error: []
})
return {
logger: {
info: (message: string) =>
calls.update((c) => ({
...c,
info: [...c.info, message]
})),
error: (message: string) =>
calls.update((c) => ({
...c,
error: [...c.error, message]
}))
},
getCalls: () => calls.get
}
})
)
// Test with spy
describe("User Service", () => {
it("should log user creation", async () => {
const program = Effect.gen(function* () {
const spy = yield* LoggerSpy
const service = yield* UserService
yield* service.createUser({ name: "Alice" })
const calls = yield* spy.getCalls()
return calls
}).pipe(
Effect.provide(Layer.merge(LoggerSpy, UserServiceLive))
)
const calls = await Effect.runPromise(program)
expect(calls.info).toContain("Creating user: Alice")
})
})
```
## Testing Error Scenarios
### Testing Expected Errors
```typescript
import { Effect } from "effect"
import { describe, it, expect } from "vitest"
describe("Error Handling", () => {
it("should handle NotFoundError", async () => {
const program = Effect.gen(function* () {
const result = yield* fetchUser("999").pipe(
Effect.catchTag("NotFoundError", (error) =>
Effect.succeed({ id: "default", name: "Guest" })
)
)
return result
})
const result = await Effect.runPromise(program.pipe(
Effect.provide(TestLayer)
))
expect(result.name).toBe("Guest")
})
it("should propagate unhandled errors", async () => {
const program = Effect.gen(function* () {
const result = yield* fetchUser("999")
return result
})
await expect(
Effect.runPromise(program.pipe(
Effect.provide(TestLayer)
))
).rejects.toThrow()
})
})
```
### Testing Error Recovery
```typescript
import { Effect } from "effect"
import { describe, it, expect } from "vitest"
describe("Retry Logic", () => {
it("should retry on network error", async () => {
let attempts = 0
const unstableOperation = Effect.gen(function* () {
attempts++
if (attempts < 3) {
return yield* Effect.fail({ _tag: "NetworkError" })
}
return yield* Effect.succeed("Success")
})
const program = unstableOperation.pipe(
Effect.retry(Schedule.recurs(5))
)
const result = await Effect.runPromise(program)
expect(result).toBe("Success")
expect(attempts).toBe(3)
})
})
```
## Testing Concurrent Code
### Testing Parallel Execution
```typescript
import { Effect, Ref } from "effect"
import { describe, it, expect } from "vitest"
describe("Concurrent Operations", () => {
it("should process items in parallel", async () => {
const program = Effect.gen(function* () {
const processed = yield* Ref.make<string[]>([])
const items = ["a", "b", "c", "d", "e"]
yield* Effect.all(
items.map((item) =>
Effect.gen(function* () {
yield* Effect.sleep("10 millis")
yield* processed.update((p) => [...p, item])
})
),
{ concurrency: "unbounded" }
)
return yield* processed.get
})
const result = await Effect.runPromise(program)
expect(result).toHaveLength(5)
expect(result).toContain("a")
expect(result).toContain("b")
})
})
```
### Testing Fiber Interruption
```typescript
import { Effect, Fiber, Ref } from "effect"
import { describe, it, expect } from "vitest"
describe("Interruption", () => {
it("should interrupt long-running task", async () => {
const program = Effect.gen(function* () {
const completed = yield* Ref.make(false)
const fiber = yield* Effect.fork(
Effect.gen(function* () {
yield* Effect.sleep("1 second")
yield* completed.set(true)
})
)
yield* Effect.sleep("100 millis")
yielRelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.