# Effect TS Testing

> Testing guidelines and patterns for Effect-based code.

- Skill: `majiayu000/effect-ts-testing` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add majiayu000/effect-ts-testing`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/effect-ts-testing/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/effect-ts-testing

---


# Effect-TS Testing Skill

Use this skill when writing or reviewing tests for Effect-based code.

## Effect testing rules (for `*.spec.*` files)

When testing a method that returns an `Effect` type, use the `@effect/vitest`
package.

- Use `import { it } from '@effect/vitest'` as opposed to importing `it` from
  regular vitest.
- Usage:
  ```typescript
  it.effect('test name', () => {
     return Effect.gen(...)
  })
  ```
- Use `it.layer()` to provide test dependencies when your Effect requires
  specific services:
  ```typescript
  it.layer(Layer.fresh(IncrementalIdGenerator))((it) => {
    it.effect('test name', () => {
      return Effect.gen(...)
    })
  })
  ```

## Testing for type-safety

Use `tsafe` package for compile-time type assertions to validate type safety in
tests.

- Import: `import { assert } from 'tsafe'` and `import type { Equals } from 'tsafe'`
- Usage: `assert<Equals<ActualType, ExpectedType>>()` - validates exact type
  equality at compile time

## Test time helpers

Never use `new Date()` in tests. Always use `yield* DateTime.now` and convert to
`Date` if needed.

```typescript
import { DateTime, Effect } from 'effect';

const nowDateTime = yield* DateTime.now;
const now = DateTime.toDateUtc(nowDateTime);
```

## Schema decoding with decodeUnknown

When decoding unknown values using Effect schemas, always use the
`decodeUnknown` function exported from `zerospin` instead of
`Schema.decodeUnknown` directly.

- Import: `import { decodeUnknown } from 'zerospin'`
- Always specify `onExcessProperty`: `'error'`, `'ignore'`, or `'preserve'`
  based on your needs
- Map errors to `ZerospinError` with code `'failed-to-decode-unknown'`

**In Effect.gen contexts:**
```typescript
import { decodeUnknown } from 'zerospin';
import { ZerospinError } from '@zerospin/error';
import { Effect } from 'effect';

const decoded = yield* decodeUnknown({
  onExcessProperty: 'error', // or 'ignore' or 'preserve'
  schema: MySchema,
  value: unknownValue,
}).pipe(
  Effect.mapError((error) => {
    return new ZerospinError({
      code: 'failed-to-decode-unknown',
      message: error.message,
      cause: error,
    });
  })
);
```

**In async/await contexts:**
```typescript
import { decodeUnknown } from 'zerospin';
import { ZerospinError } from '@zerospin/error';
import { Effect } from 'effect';

const decoded = await Effect.runPromise(
  decodeUnknown({
    onExcessProperty: 'error',
    schema: MySchema,
    value: unknownValue,
  }).pipe(
    Effect.mapError((error) => {
      return new ZerospinError({
        code: 'failed-to-decode-unknown',
        message: error.message,
        cause: error,
      });
    })
  )
);
```

