effect-testing
Comprehensive testing patterns for Effect-TS services, errors, layers, and effects. Use this skill when writing tests for Effect-based code.
What this skill does
# Effect-TS Testing Patterns
Comprehensive testing patterns for Effect-TS services, errors, layers, and effects. Use this skill when writing tests for Effect-based code.
## Core Testing Setup
### @effect/vitest Integration
```typescript
import { describe, it, expect } from "@effect/vitest"
import { Effect } from "effect"
// Basic test - it.effect provides TestContext automatically
it.effect("test name", () =>
Effect.gen(function* () {
const result = yield* someEffect
expect(result).toBe(expected)
})
)
// Test with layers - provide dependencies to all tests
it.layer(MyServiceLive)("test with service", () =>
Effect.gen(function* () {
const service = yield* MyService
const result = yield* service.doSomething()
expect(result).toBe(expected)
})
)
// Scoped tests - automatically handles resource cleanup
it.scoped("test with resources", () =>
Effect.gen(function* () {
const resource = yield* acquireResource
// resource automatically released after test
yield* useResource(resource)
})
)
```
**Key Features:**
- `it.effect` - automatic TestContext provision (TestClock, TestRandom, etc.)
- `it.layer` - provide layers to test suite, shared across tests
- `it.scoped` - automatic resource cleanup
- Full fiber dumps with causes, spans, and logs for better errors
## Testing Services
### Mock Service with Layer.succeed
```typescript
import { Effect, Context, Layer } from "effect"
// Service definition
class DatabaseService extends Context.Tag("DatabaseService")<
DatabaseService,
{
readonly query: (sql: string) => Effect.Effect<unknown>
}
>() {}
// Live implementation (production)
export const DatabaseServiceLive = Layer.succeed(
DatabaseService,
{
query: (sql) => Effect.promise(() => realDatabase.query(sql))
}
)
// Test implementation (mocked)
export const DatabaseServiceTest = Layer.succeed(
DatabaseService,
{
query: (sql) => Effect.succeed({ rows: [{ id: 1, name: "test" }] })
}
)
// Usage in test
it.layer(DatabaseServiceTest)("queries database", () =>
Effect.gen(function* () {
const db = yield* DatabaseService
const result = yield* db.query("SELECT * FROM users")
expect(result).toEqual({ rows: [{ id: 1, name: "test" }] })
})
)
```
**Convention:** Use "Live" suffix for production, "Test" suffix for mocks.
### Mock Service with Layer.mock
```typescript
import { Layer } from "effect"
// Partial mock - only implement methods you need
const PartialDatabaseMock = Layer.mock(DatabaseService, {
query: (sql) => Effect.succeed({ rows: [] })
// Other methods throw UnimplementedError when called
})
// Full mock with test doubles
const MockWithSpy = Layer.succeed(DatabaseService, {
query: vi.fn().mockReturnValue(Effect.succeed({ rows: [] }))
})
```
### Testing Services with Dependencies
```typescript
class UserService extends Context.Tag("UserService")<
UserService,
{
readonly getUser: (id: number) => Effect.Effect<User, UserNotFound>
}
>() {}
class EmailService extends Context.Tag("EmailService")<
EmailService,
{
readonly sendEmail: (to: string, body: string) => Effect.Effect<void>
}
>() {}
// Service that depends on other services
class NotificationService extends Context.Tag("NotificationService")<
NotificationService,
{
readonly notifyUser: (userId: number) => Effect.Effect<void, UserNotFound>
}
>() {
static Live = Layer.effect(
NotificationService,
Effect.gen(function* () {
const users = yield* UserService
const email = yield* EmailService
return {
notifyUser: (userId) =>
Effect.gen(function* () {
const user = yield* users.getUser(userId)
yield* email.sendEmail(user.email, "Notification")
})
}
})
)
}
// Test with all dependencies mocked
const TestLayer = Layer.mergeAll(
UserServiceTest,
EmailServiceTest
).pipe(Layer.provideMerge(NotificationService.Live))
it.layer(TestLayer)("sends notification", () =>
Effect.gen(function* () {
const notif = yield* NotificationService
yield* notif.notifyUser(1)
// Verify email was sent using mocked EmailService
})
)
```
## Testing Errors
### Expected Error Testing
```typescript
import { Effect, Exit } from "effect"
class MyError extends Data.TaggedError("MyError")<{
readonly message: string
}> {}
it.effect("handles expected errors", () =>
Effect.gen(function* () {
const result = yield* Effect.exit(
Effect.fail(new MyError({ message: "test error" }))
)
// Check error occurred
expect(Exit.isFailure(result)).toBe(true)
// Check error type
if (Exit.isFailure(result)) {
const cause = result.cause
expect(Cause.isFailType(cause)).toBe(true)
// Extract error value
const error = Cause.failureOption(cause)
expect(error).toBeSome()
expect(Option.getOrThrow(error)).toBeInstanceOf(MyError)
}
})
)
// Alternative: use catchTag to verify error
it.effect("catches specific error type", () =>
Effect.gen(function* () {
let caught = false
yield* effectThatFails.pipe(
Effect.catchTag("MyError", (error) =>
Effect.sync(() => {
caught = true
expect(error.message).toBe("test error")
})
)
)
expect(caught).toBe(true)
})
)
```
### Multiple Error Types
```typescript
type UserServiceError = UserNotFound | DatabaseError | ValidationError
it.effect("handles multiple error types", () =>
Effect.gen(function* () {
const result = yield* Effect.either(service.getUser(999))
expect(Either.isLeft(result)).toBe(true)
if (Either.isLeft(result)) {
// Pattern match on error type
const error = result.left
if (error._tag === "UserNotFound") {
expect(error.userId).toBe(999)
}
}
})
)
```
### Defect Testing (Unexpected Errors)
```typescript
it.effect("handles unexpected errors (defects)", () =>
Effect.gen(function* () {
const result = yield* Effect.exit(
Effect.die(new Error("Unexpected"))
)
expect(Exit.isFailure(result)).toBe(true)
if (Exit.isFailure(result)) {
expect(Cause.isDie(result.cause)).toBe(true)
}
})
)
```
## Testing with TestClock
```typescript
import { TestClock } from "effect"
it.effect("delays execution", () =>
Effect.gen(function* () {
let executed = false
// Fork effect that delays 1 second
const fiber = yield* Effect.fork(
Effect.delay("1 second")(
Effect.sync(() => { executed = true })
)
)
// Verify not executed yet
expect(executed).toBe(false)
// Advance time by 1 second
yield* TestClock.adjust("1 second")
// Wait for fiber to complete
yield* Fiber.join(fiber)
// Verify executed after time advance
expect(executed).toBe(true)
})
)
// Testing intervals
it.effect("processes scheduled tasks", () =>
Effect.gen(function* () {
const results: number[] = []
const fiber = yield* Effect.fork(
Effect.repeat(
Effect.sync(() => results.push(Date.now())),
Schedule.spaced("100 millis")
).pipe(Effect.timeout("1 second"))
)
// Advance time in increments
yield* TestClock.adjust("100 millis")
yield* TestClock.adjust("100 millis")
yield* TestClock.adjust("100 millis")
yield* Fiber.join(fiber)
expect(results.length).toBeGreaterThan(0)
})
)
```
## Testing with TestRandom
```typescript
import { TestRandom } from "effect"
it.effect("deterministic random values", () =>
Effect.gen(function* () {
// Set fixed random seed for reproducibility
yield* TestRandom.setSeed(42)
const random1 = yield* Random.next
const random2 = yield* Random.next
// Reset seed - same values again
yield* TestRandom.setSeed(42)
const random3 = yield* Random.next
expect(random3).toBe(random1)
})
)
```
## Testing Layers
### Fresh Layers Per Test
```typescript
// Helper to create fresh layer for each test
const makeFreshLRelated 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.