Tests prove behavior through supplied services

A service is a named interface. A Layer describes how to build a service and supply its dependencies. A Scope20 owns cleanup. A capability is a named action; a projection converts it into an interface.ΒΆ

Name the behavior that lacks proof before adding a test. Supply test services through Layers, return an Effect, and let @effect/vitest own its execution.ΒΆ

Test behavior independently of the implementation

A test that repeats the implementation cannot detect the same mistake in both places. A snapshot that only records a changed file does not prove a domain rule. If the existing suite already covers the reported behavior, fix the cause without adding a duplicate test.ΒΆ

Our projection tests execute the produced CLI command, HTTP route, MCP tool, RPC call, or sandbox call. They check results and declared failures. Returning a projection object does not establish correct behavior.ΒΆ

Run an Effect through supplied services

it.effect runs the Effect returned by the test. it.layer supplies services and their dependencies to a block of tests. The file-inspection integration test uses real platform services and a scoped temporary file:ΒΆ

packages/core/test/file-inspector.test.ts Β· 65e9465f
import { FileInspector } from "../src/index.js";
it.layer(TestLayer)("FileInspector", (test) => {
  test.effect("reads a file through the Effect filesystem", () =>
    Effect.gen(function* readsThroughFileSystem() {
      const file = yield* writeTempFile("notes.txt", "one two\nthree\n");
      const inspector = yield* FileInspector;

      const stats = yield* inspector.inspect(file);

      expect(stats).toMatchObject({ bytes: 14, lines: 2, words: 3 });
    })
  );

Share a Layer when tests need a common expensive resource. Do not assume its mutable state resets between tests in the same block. Use fresh construction or explicit reset when the behavior needs isolation.ΒΆ

TestClock controls Effect time for sleeps and retries. Use it instead of waiting for wall-clock delays in a behavior test. Use it.live for an integration test that needs live runtime services.ΒΆ

Choose the smallest proof that fits the rule

BehaviorTest shape
One interface between componentsAn integration example with supplied services
A rule over many inputsit.prop or it.effect.prop
State that depends on earlier actionsA model checked after generated commands
UI behaviour, timers, and racesA Foldkit Scene

Generated inputs do not create the assertion. The test still needs an independent statement of correct behavior.ΒΆ

Arbitrary.schema derives input generators from Schema. Pure properties use it.prop; properties returning Effects use it.effect.prop. The events suite generates requests and checks that capture preserves the response while excluding private data.ΒΆ

Keep the assertion independent

The sensitive-query property generates a known sensitive key and requires its removal:ΒΆ

packages/events/test/request-facts.test.ts Β· 65e9465f
  it.prop(
    "credentials, emails, codes, and session ids in a query never reach an event",
    {
      pairs: queryPairs,
      secret: sensitiveWord,
      value: word,
    },
    ({ pairs, secret, value }) => {
      const captured = capturedQuery(
        new URLSearchParams([...pairs, [secret, value]])
      );

      expect(Object.hasOwn(captured, secret)).toBe(false);
    }
  );

The assertion does not call isSensitiveKey to decide whether removal was required. The independent generator includes keys that the capture filter must reject.ΒΆ

When the property was first written, deleting token from the filter did not fail a test that reused that same filter. An independent sensitive-key property closed the gap. Property-based testing18 records that planted violation and the smaller counterexample.ΒΆ

Generate histories when order matters

Visitor identity depends on the device, retained cookie, and day boundary. The model generates action sequences rather than picking one successful sequence:ΒΆ

packages/events/test/visitor.model.test.ts Β· 65e9465f
const Visit = Schema.Struct({
  device: Schema.Literals([0, 1, 2]),
  keepsCookie: Schema.Boolean,
  startsNewDay: Schema.Boolean,
});

const commands = Arbitrary.array(Arbitrary.schema(Visit), { maxLength: 30 });
  it.effect.prop(
    "persistent ids survive every visit that keeps the cookie and are reissued when it is lost",
    { steps: commands },
    ({ steps }) => runSession("persistent", steps)
  );

  it.effect.prop(
    "daily ids are stable within a day, never set a cookie, and never repeat across days",
    { steps: commands },
    ({ steps }) => runSession("daily", steps)
  );
});

Model-based testing14 explains the assertions and records the failed day-boundary mutation. Agents explore, tests remember7 explains how exploration can expose a behavior gap worth making repeatable.ΒΆ

Common wrong approach

Plant a violation in the behavior it claims to protect. Observe failure, then restore the implementation and observe success.ΒΆ

Do not rewrite the test’s expected result to match a broken implementation. Keep the counterexample or missing assertion that explains the behavior gap.ΒΆ

Effect idiom and house rule

See also: properties18, models14, scenes7, exploration7, and the fence39.ΒΆ

Read why dependency injection matters14.ΒΆ

Sources

  1. Effect contributors. 2026. Effect Vitest integration.. Effect-TS. it.effect, it.layer, it.prop, and effectful properties expose Effect-aware test execution. Accessed 2026-10-06.
  2. Effect contributors. 2026. Writing Effect tests.. Effect-TS. TestClock controls Effect time, and effect properties can use Schema-based inputs. Accessed 2026-10-06.
  3. Effect contributors. 2026. Layer isolation tests.. Effect-TS. Separate it.layer blocks construct isolated service state. A shared block is not a per-test reset. Accessed 2026-10-06.
  4. Effect contributors. 2026. Arbitrary. Effect 4.0.0.. Effect-TS. Schema-derived generators, generated command arrays, and shrinking support properties and history models. Accessed 2026-10-06.
  5. rat-stack contributors. 2026. Events behavior tests.. rat-stack. An independent sensitive-key property and generated visitor histories exercise domain behavior. Accessed 2026-10-06.
  6. rat-stack contributors. 2026. Agent instructions.. rat-stack. Testing policy rejects tautologies and change detectors, prefers properties/models, and requires planted violations for new properties. Accessed 2026-10-06.
  7. Langton, Kit. 2026. Testing. Effect Solutions.. Kit Langton. Topic framing for Effect-aware tests and replaceable services; the property/model standard is rat-stack's policy. Accessed 2026-10-06.

Last changed 2026-10-09 in 3107109. Source on GitHub. Change log.

Linked from

  1. A service captures its construction dependencies. Build a service with its dependencies, then expose methods that callers can use without rebuilding the graph.

    See also: domain structure, Layer composition, the Layer constructor pattern, and testing through services.ΒΆ

  2. Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
    Link context

    Tests prove behavior through supplied services β€” Supply services through Layers, choose properties or models for domain rules, and reject tests that cannot detect broken behavior.

  3. Agents explore, tests remember. Let agent users explore scenarios; turn a good bug reproduction into a repeatable test.

    For executable Effect tests, read tests through supplied services.ΒΆ

  4. Change log. What changed in the files served here, newest first.

    Tests prove behavior through supplied services β€” edited: docs(skills): add test-behaviour skill and the UI behaviour is a scene lore page

  5. Code snippets quote a pinned source. Shipped design brief: pinned Git objects, typed diagnostics, and one build-only snippet model for both views.

    See model-based testing and tests that earn their place.ΒΆ

  6. Configuration is a supplied, validated dependency. Parse configuration when a Layer builds, reject invalid values, and supply controlled settings in tests.

    See also: services, Layer composition, failures, and tests.ΒΆ

  7. Dependency injection is the reason to choose Effect. Effect tracks service requirements in types and supplies replaceable implementations through Layers.

    Read tests through supplied services.ΒΆ

  8. Effect basics. An Effect describes work, expected failures, and required services before a runtime executes it.

    See tests through supplied services.

  9. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  10. Layer composition makes dependencies explicit. Supply a service's dependencies, choose which services remain visible, and reuse Layer identity within a build.

    See also: service construction, domain structure, cartridges, and testing.ΒΆ

  11. learn-rat-stack. Trace a capability through Effect, XState, five projections, the CLI, and Alchemy. Try hosted search and read.

    For implementation tasks, use services, Layer composition, and testing.ΒΆ

  12. Model-based testing. Describe what a system should do as a small model, generate action sequences, and check the real system against the model.

    See also: Tests through supplied services.ΒΆ

  13. No comments. Code comments are banned because names, types, tests, and boundaries should carry the meaning.

    See the fence for how the rule is enforced and tests that earn their place for the test that can replace a comment.ΒΆ

  14. Property-based testing. State a rule that must hold for every input, then let a generator hunt for the smallest input that breaks it.

    See tests that earn their place.ΒΆ

  15. Rat Stack lore | rat-stack. Short, source-grounded notes on the ideas and decisions behind rat-stack.
    Link context

    Tests prove behavior through supplied services β€” Supply services through Layers, choose properties or models for domain rules, and reject tests that cannot detect broken behavior.

  16. Run Effect at the integration boundary. Build the runtime where a calling framework enters, reuse its services, and dispose it when that integration ends.

    In rat-stack, test harnesses use Effect-aware tests, not manually created runtimes.ΒΆ

  17. Schemas define the boundary. Define accepted data with Schema, decode unknown input before dispatch, and encode results through the declared contract.

    See also: one schema, three surfaces, OpenAPI refusals, services, and testing.ΒΆ

  18. Source change history (log.md). The generated change log as Markdown.

    Tests prove behavior through supplied services β€” edited: docs(skills): add test-behaviour skill and the UI behaviour is a scene lore page

  19. Structure an Effect codebase by domain. Start with folders for application work, services and Layers in the same file, and startup code that supplies implementations.

    Supply test services through Layers.ΒΆ

  20. test-behaviour. Pick the test shape that proves a behaviour. Use when you ask "how do I test this?", add a test, test a domain rule, a lifecycle, a retry, a session, a UI interaction, a timer, or a race, or review a test that might be tautological.

    Read tests that earn their place first.ΒΆ

  21. The fence. The fence uses compiler checks, lint rules, and hooks to reject unsafe shortcuts.

    See tests that earn their place for the testing law.ΒΆ

  22. UI behaviour is a scene. A Foldkit Scene drives update, view, and subscriptions with input and time as plain data, then asserts on what the view shows.

    See also: tests that earn their place, model-based testing, and the test-behaviour skill.ΒΆ

Unlinked mentions (1)