Nanook

A toolkit for test case and test data creation

Combine the power of equivalence class tables and data generators.

See the use cases ↓

npm install @xhubio/nanook-table

# Node.js >= 22. One ESM package, types included.
# Everything is imported from that one module:
#   TestcaseProcessor, FileProcessor, ImporterXlsx,
#   ParserDecision, ParserMatrix, ParserSpecification,
#   DataGeneratorRegistry, DataGeneratorBase,
#   GeneratorFaker, LoggerMemory, TableDecision,
#   TableMatrix, …

From the README of @xhubio/nanook-table 3.0.1.

$ claude
> /createEquivalenceClassTable Login Form

  17.5 minutes, 56 turns, no further input

  resources/login-form-tests.xlsx
    User   7 columns, 15 combinations, 100 %
    Login  7 columns, 48 combinations, 100 %
  scripts/create-login-form-table.ts
  scripts/check-login-form-table.ts
  scripts/generate-login-form-fixtures.ts

$ node scripts/generate-login-form-fixtures.ts
  11 fixtures written to fixtures/login-form

Summary of the run behind the Quickstart with Claude Code (2 September 2026). The skill has shipped with the package since 2.1.0.

import {
  LoggerMemory, FileProcessor, ImporterXlsx,
  ParserDecision, DataGeneratorRegistry, GeneratorFaker,
  TestcaseProcessor, type InterfaceWriter
} from '@xhubio/nanook-table'

const logger = new LoggerMemory()
logger.writeConsole = true

const fileProcessor = new FileProcessor({ logger })
fileProcessor.registerImporter(
  'xlsx',
  new ImporterXlsx()
)
fileProcessor.registerParser(
  '<DECISION_TABLE>',
  new ParserDecision({ logger })
)
await fileProcessor.load(['resources/login.xlsx'])

const registry = new DataGeneratorRegistry()
registry.registerGenerator(
  'faker',
  new GeneratorFaker({ logger })
)

const collected: unknown[] = []
const writer: InterfaceWriter = {
  logger,
  async before() {},
  async write(tc) {
    collected.push(JSON.parse(JSON.stringify(tc)))
  },
  async after() {},
}

const tables = Object.fromEntries(
  fileProcessor.tables.map((t) => [t.tableName, t])
)

const processor = new TestcaseProcessor({
  logger,
  tables,
  generatorRegistry: registry,
  writer: [writer],
})
await processor.process()

console.log(JSON.stringify(collected[0], null, 2))

Adapted from the script that generated the login example for the blog: package imports instead of source imports and without its custom len generator, otherwise the same calls. Runs on @xhubio/nanook-table 3.0.1.

{
  "tableName": "Login",
  "name": "OK_login",
  "data": {
    "Login": {
      "4679e9a0-6d2d-4fea-8351-9677ed8573b6": {
        "account": "active",
        "Expected reaction": [
          {
            "key": "signed in",
            "comment": "session cookie is set"
          }
        ],
        "Expected effect": [
          {
            "key": "failed-attempt counter unchanged",
            "comment": "success or unknown user"
          }
        ],
        "email": "Jennifer_Tremblay@hotmail.com",
        "password": "VuZGJBushpsbY_b"
      }
    }
  },
  "instanceId": "4679e9a0-6d2d-4fea-8351-9677ed8573b6",
  "callTree": {
    "instanceId": "218e4109-1b41-4269-8cac-d10c92551e39",
    "tableName": "Login",
    "testcaseName": "OK_login",
    "neverExecute": false,
    "tags": [],
    "children": []
  },
  "postProcessDirectives": []
}

What the script prints: the first test case of that run, e-mail and password from the Faker generator. Nanook's default writer would store each one as tdg/<name>/testcaseData.json.

What is Nanook?

Nanook is an open-source toolkit for defining test cases in spreadsheets and generating test data from them. You describe test scenarios as equivalence class tables, matrix tables or specification tables in standard XLSX files; Nanook reads those sheets, runs every test case through pluggable data generators and hands the result to one or more writers — JSON files by default, or any format you implement. Version 3 is a single ESM package written in TypeScript, with type declarations, for Node.js 22 or newer. Generation is a plain script, so it runs in any CI job. Nanook is used by enterprises including Deutsche Bahn for automated test data generation.

Nanook architecture diagram
System architecture

Choose where to start

Three table types and one processing pipeline. Each card opens the matching guide.

Decision tables

Equivalence class tables: fields, their valid and invalid classes, and one column per test case marking which class applies. The table is both the specification and the test plan.

Matrix and specification tables

Matrix tables describe pairwise or combinatorial relationships between two dimensions of test parameters. Specification tables declare field rules and severities and are converted into a decision table automatically.

Generators, writers and filters

A cell directive such as gen:1:faker:internet.email calls the built-in Faker generator or your own; a shared instance ID keeps related fields on one record. Every test case goes to the registered writers — JSON by default, anything with a custom writer. Tag-based filters choose which test cases are processed.

How it works

Three steps from a spreadsheet to generated test data.

Write the test cases as a decision table in any editor that saves XLSX — Excel, LibreOffice Calc, or Google Sheets via download. The sheet starts with the marker <DECISION_TABLE>, lists the fields with their equivalence classes and has one column per test case. Matrix and specification tables live in the same workbook. No code at this stage; the Claude Code skill shipped with the package can draft the table for you; see the Quickstart with Claude Code.

Put a directive into the generator column of each equivalence class: a static value, gen:<instanceId>:<generator>:<parameter> to call a generator, or ref: to reuse a value from another test case. The built-in Faker generator covers names, e-mail addresses, dates and more; a custom generator is a TypeScript class that extends DataGeneratorBase and overrides doGenerate(). The same instance ID across fields yields one coherent record.

Run the script: the file processor loads the workbook, TestcaseProcessor resolves references, calls the generators, applies tag filters and passes every test case to the registered writers. The default writer stores one JSON file per test case under tdg/<name>/testcaseData.json; a custom writer implements before(), write() and after() and can produce CSV, database rows or API calls. It is a plain Node.js script, so it runs in any CI job.

Use cases

Eight scenarios where one table replaces hand-written fixtures.

API testing

Generate request payloads for every valid and invalid combination of an endpoint's fields from one decision table. A specification table turns field rules — type, mandatory, min, max, format — into those classes automatically.

Form validation

Registration, login, checkout. Every form has fields with validation rules. One decision table yields test data for every valid and invalid combination, without hand-written fixtures.

CI/CD integration

Regenerate test data on every spec change. Nanook is an ESM package for Node.js 22 or newer; the generation script runs in any pipeline job — GitHub Actions, GitLab CI, Jenkins, or a cron job.

GDPR-compliant test data

Stop copying production data into test environments. Generated records match your schema, and the Faker generator produces names, addresses and e-mail addresses that belong to nobody.

End-to-end test suites

Decision tables as the single source of a Playwright suite: in our own SaaS, 16 hand-written spec files became 117 decision tables and 1,981 data-driven cases. Read the field report.

Integration testing

Data that flows through several services stays consistent: ref: directives pull values from test cases in other tables, and shared instance IDs keep one generated record coherent across fields.

Environment seeding

Bootstrap dev and staging environments from one spreadsheet. A custom writer can insert the generated records straight into a database, and a persistent generator store keeps generated values across runs.

Migration testing

Test database migrations with generated data that matches your schema. Change the table when the schema changes and regenerate — no production data involved.

Frequently asked questions

What is an equivalence class table?

A spreadsheet that lists the input fields of a system, the classes of values each field can take — valid and invalid — and one column per test case marking which class applies. Nanook calls it a decision table; the sheet starts with the marker <DECISION_TABLE>.

Which spreadsheet formats does Nanook support?

XLSX. The default file processor registers the XLSX importer for the extensions .xlsx and .xls; there is no ODS or Google Sheets importer. Use any editor that saves XLSX — Excel, LibreOffice Calc, or Google Sheets via download — or register your own importer for another format.

What output formats can Nanook generate?

By default one JSON file per test case, written to tdg/<name>/testcaseData.json. Output is produced by writers: implement before(), write() and after() of InterfaceWriter to produce CSV, database rows, API calls or any other format. Several writers can run in one pass.

Which language and runtime does Nanook need?

Nanook 3 is written in TypeScript and ships as one ESM package with type declarations. It requires Node.js 22 or newer. Version 2.0 (March 2026) was the rewrite from JavaScript; the 1.x names Processor and TDGServiceRegistry no longer exist — the entry point is TestcaseProcessor.

Can I use Nanook in my CI/CD pipeline?

Yes. Generation is a Node.js script without service dependencies, so it runs in any pipeline job. Regenerate the test data whenever the tables change.

Can an AI assistant create the tables?

Yes. Since version 2.1.0 the repository ships a Claude Code skill (create-equivalence-class-table) and a slash command (/createEquivalenceClassTable) that draft a formatted decision table for a page, form or endpoint. The blog post AI-Assisted Equivalence Class Tables with Claude Code shows a run. The skill also ships inside the npm package: copy it into your project’s .claude folder and follow the Quickstart with Claude Code.

Is Nanook free to use?

Yes, Nanook is open source and released under the MIT License. It is free for both personal and commercial use.