CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-jest-each

Parameterised tests for Jest that enable running the same test multiple times with different data sets using arrays or tagged template literals

85

1.10x
Overview
Eval results
Files

index.mddocs/

jest-each

jest-each is a parameterized testing library that enables running the same test multiple times with different data sets. It provides two main approaches: array-based parameterization and tagged template literal syntax with readable table formatting. The library integrates seamlessly with all Jest test functions and modifiers.

Package Information

  • Package Name: jest-each
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install jest-each

Core Imports

import each from "jest-each";

For CommonJS:

const each = require("jest-each").default;

Named imports:

import each, { bind } from "jest-each";

Basic Usage

Array Format

import each from "jest-each";

// Array of arrays
each([
  [1, 2, 3],
  [4, 5, 9],
  [7, 8, 15]
]).test('adds %i + %i to equal %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// Array of objects  
each([
  { name: 'John', age: 25 },
  { name: 'Jane', age: 30 }
]).test('user $name is $age years old', ({ name, age }) => {
  expect(typeof name).toBe('string');
  expect(typeof age).toBe('number');
});

Template Literal Format

each`
  a    | b    | expected
  ${1} | ${2} | ${3}
  ${4} | ${5} | ${9}
  ${7} | ${8} | ${15}
`.test('adds $a + $b to equal $expected', ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});

Capabilities

Main Each Function

Creates parameterized test functions that work with all Jest test methods and modifiers.

/**
 * Main function to create parameterized tests using arrays or template literals
 * @param table - Test data as array of arrays, array of objects, or template literal
 * @param data - Additional template data when using template literals
 * @returns Object with Jest test methods (test, it, describe, etc.)
 */
function each(table: EachTable, ...data: TemplateData): {
  test: EachTestFunction;
  it: EachTestFunction;
  describe: EachDescribeFunction;
  xit: EachTestFunction;
  fit: EachTestFunction;
  xtest: EachTestFunction;
  fdescribe: EachDescribeFunction;
  xdescribe: EachDescribeFunction;
};

type EachTable = ArrayTable | TemplateTable;
type ArrayTable = Table | Row;
type Table = ReadonlyArray<Row>;
type Row = ReadonlyArray<unknown>;
type TemplateTable = TemplateStringsArray;
type TemplateData = ReadonlyArray<unknown>;

With Global Context

Creates each function bound to a specific global context for custom Jest environments.

/**
 * Creates each function bound to specific global context
 * @param g - Global Jest context object
 * @returns Function that accepts table and returns test methods
 */
each.withGlobal: (g: Global) => (table: EachTable, ...data: TemplateData) => ReturnType<typeof each>;

interface Global {
  test: TestFunction;
  it: TestFunction;
  describe: DescribeFunction;
  xit: TestFunction;
  fit: TestFunction;
  xtest: TestFunction;
  fdescribe: DescribeFunction;
  xdescribe: DescribeFunction;
}

Test Function Interface

All returned test functions support Jest's standard interface with modifiers.

interface EachTestFunction {
  /**
   * Creates a parameterized test
   * @param title - Test title with optional placeholders
   * @param testFn - Test function receiving parameters from data table
   * @param timeout - Optional timeout in milliseconds
   */
  (title: string, testFn: EachTestFn<TestFn>, timeout?: number): void;
  
  /** Run only this test */
  only: EachTestFunction;
  
  /** Skip this test */
  skip: EachTestFunction;
  
  /** Concurrent test execution */
  concurrent: EachConcurrentTestFunction;
}

interface EachConcurrentTestFunction {
  (title: string, testFn: EachTestFn<ConcurrentTestFn>, timeout?: number): void;
  only: EachConcurrentTestFunction;
  skip: EachConcurrentTestFunction;
}

interface EachDescribeFunction {
  /**
   * Creates a parameterized describe block
   * @param title - Describe title with optional placeholders
   * @param suiteFn - Suite function receiving parameters from data table
   * @param timeout - Optional timeout in milliseconds
   */
  (title: string, suiteFn: EachTestFn<BlockFn>, timeout?: number): void;
  
  /** Run only this describe block */
  only: EachDescribeFunction;
  
  /** Skip this describe block */
  skip: EachDescribeFunction;
}

type EachTestFn<T extends TestCallback> = (...args: ReadonlyArray<any>) => ReturnType<T>;

Bind Function (Advanced)

Core binding function for creating custom parameterized test functions.

/**
 * Creates parameterized test functions by binding to Jest global methods
 * @param cb - Global Jest callback function to bind to
 * @param supportsDone - Whether the callback supports done callback (default: true)
 * @param needsEachError - Whether to pass error context (default: false)
 * @returns Function that accepts table data and returns bound test function
 */
function bind<EachCallback extends TestCallback>(
  cb: GlobalCallback,
  supportsDone?: boolean,
  needsEachError?: boolean
): EachTestFn<any>;

type GlobalCallback = (
  testName: string,
  fn: ConcurrentTestFn,
  timeout?: number,
  eachError?: Error
) => void;

type TestCallback = BlockFn | TestFn | ConcurrentTestFn;

Title Formatting and Interpolation

Printf-Style Placeholders

jest-each supports printf-style placeholders for automatic value formatting in test titles:

  • %s - String formatting
  • %d, %i - Integer formatting
  • %f - Float formatting
  • %j - JSON formatting
  • %o, %O - Object formatting
  • %p - Pretty-print formatting (with depth control)
  • %# - Zero-based index placeholder
  • %$ - One-based index placeholder
each([
  [1, 2, 3],
  [4, 5, 9]
]).test('test %# adds %i + %i to equal %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});
// Results in: "test 0 adds 1 + 2 to equal 3", "test 1 adds 4 + 5 to equal 9"

Template Variable Interpolation

When using template literals or object arrays, variables can be interpolated using $variable syntax:

each`
  name     | age  | status
  ${'John'} | ${25} | ${'active'}
  ${'Jane'} | ${30} | ${'inactive'}
`.test('user $name (age $age) is $status', ({ name, age, status }) => {
  expect(typeof name).toBe('string');
});

Object Property Access

Template variables support deep property access using dot notation:

each([
  { user: { profile: { name: 'John' }, age: 25 } },
  { user: { profile: { name: 'Jane' }, age: 30 } }
]).test('user $user.profile.name is $user.age years old', ({ user }) => {
  expect(user).toBeDefined();
});

Data Table Formats

Array of Arrays

Most basic format for simple parameter lists:

each([
  [1, 2, 3],    // First test case
  [4, 5, 9],    // Second test case  
  [7, 8, 15]    // Third test case
]).test('adds %i + %i to equal %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

Array of Objects

Provides named parameters for better readability:

each([
  { input: 'hello', expected: 5 },
  { input: 'world', expected: 5 },
  { input: '', expected: 0 }
]).test('$input has length $expected', ({ input, expected }) => {
  expect(input.length).toBe(expected);
});

Single Column Arrays

For tests with single parameters:

each([1, 2, 3, 4, 5]).test('number %i is defined', (num) => {
  expect(num).toBeDefined();
  expect(typeof num).toBe('number');
});

Template Literals

Tabular format with column headers for maximum readability:

each`
  operation | a    | b    | expected
  ${'add'}  | ${1} | ${2} | ${3}
  ${'sub'}  | ${5} | ${3} | ${2}
  ${'mul'}  | ${3} | ${4} | ${12}
`.test('$operation: $a and $b equals $expected', ({ operation, a, b, expected }) => {
  switch (operation) {
    case 'add':
      expect(a + b).toBe(expected);
      break;
    case 'sub':
      expect(a - b).toBe(expected);
      break;
    case 'mul':
      expect(a * b).toBe(expected);
      break;
  }
});

Jest Integration

Test Methods

All Jest test methods are supported:

each(testData).test('test title', testFn);        // Standard test
each(testData).test.only('test title', testFn);   // Run only this test
each(testData).test.skip('test title', testFn);   // Skip this test
each(testData).test.concurrent('test title', testFn); // Concurrent execution

each(testData).it('test title', testFn);          // Alias for test
each(testData).xit('test title', testFn);         // Skip test (alias)
each(testData).fit('test title', testFn);         // Focus test (alias)
each(testData).xtest('test title', testFn);       // Skip test (alias)

Describe Blocks

Parameterized describe blocks for grouping related tests:

each([
  { type: 'user', endpoint: '/users' },
  { type: 'post', endpoint: '/posts' }
]).describe('$type API', ({ type, endpoint }) => {
  test('should fetch all items', async () => {
    const response = await api.get(endpoint);
    expect(response.status).toBe(200);
  });
  
  test('should create new item', async () => {
    const response = await api.post(endpoint, { name: 'Test' });
    expect(response.status).toBe(201);
  });
});

Concurrent Test Execution

Support for concurrent test execution with proper typing:

each([
  { url: '/api/users/1' },
  { url: '/api/users/2' },
  { url: '/api/users/3' }
]).test.concurrent('fetches data from $url', async ({ url }) => {
  const response = await fetch(url);
  expect(response.status).toBe(200);
});

Done Callback Support

Automatic detection and support for Jest's done callback:

each([
  { delay: 100 },
  { delay: 200 }
]).test('async test with $delay ms delay', ({ delay }, done) => {
  setTimeout(() => {
    expect(delay).toBeGreaterThan(0);
    done();
  }, delay);
});

Error Handling and Validation

Table Validation

Comprehensive validation with descriptive error messages:

// Invalid: empty array
each([]).test('title', () => {}); // Throws: "Error: .each called with an empty Array of table data."

// Invalid: tagged template with no data
each``.test('title', () => {}); // Throws: "Error: .each called with an empty Tagged Template Literal of table data."

// Invalid: not an array or template literal
each("invalid").test('title', () => {}); // Throws: ".each must be called with an Array or Tagged Template Literal."

Template Table Validation

Validation for template literal format and argument count:

// Invalid: mismatched arguments
each`
  a | b
  ${1} | ${2}
  ${3} // Missing second argument
`.test('title', () => {}); // Throws error with missing argument details

Types

// Core types for data tables
type Col = unknown;
type Row = ReadonlyArray<Col>;
type Table = ReadonlyArray<Row>;
type ArrayTable = Table | Row;
type TemplateTable = TemplateStringsArray;
type TemplateData = ReadonlyArray<unknown>;
type EachTable = ArrayTable | TemplateTable;

// Test function types
type ValidTestReturnValues = void | undefined;
type TestReturnValue = ValidTestReturnValues | Promise<unknown>;
type DoneFn = (reason?: string | Error) => void;

type TestFn = PromiseReturningTestFn | GeneratorReturningTestFn | DoneTakingTestFn;
type PromiseReturningTestFn = (this: TestContext) => TestReturnValue;
type DoneTakingTestFn = (this: TestContext, done: DoneFn) => ValidTestReturnValues;
type ConcurrentTestFn = () => Promise<unknown>;
type BlockFn = () => void;

// Generic test function type for parameterized tests
type EachTestFn<EachCallback extends TestCallback> = (
  ...args: ReadonlyArray<any>
) => ReturnType<EachCallback>;

// Template and interpolation types
type Template = Record<string, unknown>;
type Templates = Array<Template>;
type Headings = Array<string>;

// Internal test case representation
type EachTests = ReadonlyArray<{
  title: string;
  arguments: ReadonlyArray<unknown>;
}>;

Install with Tessl CLI

npx tessl i tessl/npm-jest-each

docs

index.md

tile.json