Effection Logo
@effectionx/bddv0.3.1thefrontside/effectionx
JSR BadgeNPM Badge with published version

@effectionx/bdd

A BDD (Behavior-Driven Development) testing harness for Deno that integrates seamlessly with Effection operations. This package provides a familiar describe/it/beforeEach API that works natively with Effection's generator-based operations.

Features

  • 🔄 Native Effection Support: Test functions can be generator functions that yield operations
  • 🏗️ Familiar BDD API: Uses the standard describe, it, and beforeEach functions you know and love
  • 🧹 Automatic Cleanup: Proper resource management and cleanup for Effection operations
  • 🎯 Skip and Only: Full support for .skip and .only modifiers
  • 📦 Zero Configuration: Works out of the box with Deno's built-in testing framework

Installation

Add to your deno.json imports:

{
  "imports": {
    "@effectionx/bdd": "jsr:@effectionx/bdd"
  }
}

Basic Usage

import { beforeEach, describe, it } from "@effectionx/bdd";
import { expect } from "@std/expect";
import { sleep, spawn } from "effection";
import { createSignal, is } from "@effectionx/signals";

describe("My async operations", () => {
  let counter: ReturnType<typeof createSignal<number, void>>;

  beforeEach(function* () {
    // Setup that runs before each test
    counter = yield* createSignal(0);
    yield* sleep(10); // Can use Effection operations in setup
  });

  it("should increment counter", function* () {
    // Test function is a generator that can yield operations
    counter.update((n) => n + 1);
    yield* is(counter, (value) => value === 1);
    expect(counter.valueOf()).toBe(1);
  });
});

Real-World Examples

The following packages have been migrated to use @effectionx/bdd and provide excellent examples of testing patterns:

  • stream-helpers: See batch.test.ts for testing stream batching with time and size limits
  • signals: See array.test.ts for testing array signal operations like push, set, and update
  • timebox: See timebox.test.ts for testing timeout scenarios with both success and timeout cases
  • task-buffer: See task-buffer.test.ts for testing task queuing and buffer management
  • websocket: See websocket.test.ts for testing bidirectional WebSocket communication and connection lifecycle
  • worker: See worker.test.ts for testing web worker communication, error handling, and lifecycle management

Common Patterns Demonstrated

These test files show how to:

  • Handle async operations without run() wrappers
  • Test error scenarios using try/catch blocks instead of Promise rejections
  • Use beforeEach for test setup with Effection operations
  • Wait for signal changes using the is helper
  • Test resource cleanup and proper teardown
  • Handle timeouts and concurrent operations

API Reference

describe(name: string, body: () => void)

Creates a test suite with the given name. Test suites can be nested.

Options:

  • describe.skip() - Skip this test suite
  • describe.only() - Run only this test suite

it(desc: string, body?: () => Operation<void>)

Creates a test case with the given description. The body function should be a generator function that can yield Effection operations.

Options:

  • it.skip() - Skip this test case
  • it.only() - Run only this test case

Parameters:

  • desc - Description of what the test should do
  • body - Generator function containing the test logic (optional for pending tests)

beforeEach(body: () => Operation<void>)

Registers a setup function that runs before each test in the current suite. The body function should be a generator function that can yield Effection operations.

afterEach

This package doesn't include afterEach because it's typically used for clean up. With Effection, clean up is done in finally block of the resource. Consider creating a resource in beforeEach if you encounter a need for afterEach.

beforeAll

Is not implemented yet.

Migration from Standard Deno Testing

If you're migrating from standard Deno testing with Effection, the changes are minimal:

Before:

import { describe, it } from "@std/testing/bdd";
import { run } from "effection";

describe("my tests", () => {
  it("should work", async () => {
    await run(function* () {
      const result = yield* someOperation();
      expect(result).toBe("success");
    });
  });
});

After:

import { describe, it } from "@effectionx/bdd";
// No need to import 'run'

describe("my tests", () => {
  it("should work", function* () {
    const result = yield* someOperation();
    expect(result).toBe("success");
  });
});

Contributing

This package is part of the Effection ecosystem. Contributions are welcome!

API Reference

interface SanitizeOptions

Sanitization options for test cases and test suites. These options control Deno's test sanitizers.

Properties

sanitizeExitoptional: boolean

Ensure the test case does not prematurely cause the process to exit. Defaults to true.

sanitizeOpsoptional: boolean

Check that the number of async completed ops after the test is the same as number of dispatched ops. Defaults to true.

sanitizeResourcesoptional: boolean

Ensure the test case does not "leak" resources - ie. the resource table after the test has exactly the same contents as before the test. Defaults to true.

const only: (name: string, body: () => void, options?: SanitizeOptions) => void

Type

(name: string, body: () => void, optionsoptional: SanitizeOptions) => void

function describe(name: string, body: () => void, options?: SanitizeOptions): void

Parameters

name: string

body: () => void

optionsoptional: SanitizeOptions

Return Type

void

namespace describe

Additional properties on the describe function.

Variables

v describe.skip

No documentation available.

v describe.only

No documentation available.

function beforeAll(body: () => Operation<void>): void

Parameters

body: () => Operation<void>

Return Type

void

function beforeEach(body: () => Operation<void>): void

Parameters

body: () => Operation<void>

Return Type

void

const skip: (args: Parameters<>) => ReturnType<>

Type

(...args: Parameters) => ReturnType

const only: (desc: string, body: () => Operation<void>, options?: SanitizeOptions) => void

Type

(desc: string, body: () => Operation<void>, optionsoptional: SanitizeOptions) => void

function it(desc: string, body?: () => Operation<void>, options?: SanitizeOptions): void

Parameters

desc: string

bodyoptional: () => Operation<void>

optionsoptional: SanitizeOptions

Return Type

void

namespace it

Additional properties on the it function.

Variables

v it.skip

No documentation available.

v it.only

No documentation available.