moonrockzmoonspec
v0.11.0next
Docs / Configuration Reference
On this pageConfig File (moonspec.json5 / moonspec.json)Config FieldsExample: Minimal ConfigExample: Full ConfigRunOptions (Runtime Configuration)Builder MethodsCLI Flagsmoonspec gen testsmoonspec gen stepsmoonspec gen buildmoonspec checkColorsPrecedence OrderMerge BehaviorExampleSchema

Configuration Reference

moonspec supports configuration through config files, runtime options, and CLI flags. These layers compose: config files set project defaults, CLI flags override them for one-off runs, and RunOptions controls runtime behavior programmatically.


Config File (moonspec.json5 / moonspec.json)

moonspec auto-discovers config files in the current working directory:

FileRoleFormat
moonspec.jsonBaseJSON5
moonspec.json5OverrideJSON5

Both files are parsed as JSON5, which means you can use comments, trailing commas, and unquoted keys.

When both files exist, moonspec.json serves as the base configuration and moonspec.json5 provides overrides using merge semantics (see Merge Behavior below). When only one file exists, it is used as-is.

To bypass auto-discovery entirely, pass an explicit path with the --config flag:

moonspec gen tests --config path/to/custom.json5 features/*.feature

Config Fields

{
  // World type name for codegen (e.g. "CalcWorld")
  "world": "CalcWorld",

  // Codegen mode: "per-scenario" (default) or "per-feature"
  "mode": "per-scenario",

  // Skip tags: Gherkin tags that cause scenarios to be skipped
  // Default: ["@skip", "@ignore"] (handled by RunOptions at runtime)
  "skip_tags": ["@skip", "@ignore", "@wip"],

  // Steps codegen configuration for `moonspec gen steps`
  "steps": {
    "output": "alongside",
    "exclude": ["lib/*", "vendor/*"]
  },

  // Generate tests that print reports without ANSI colors
  "no_color": false,

  // Test file kind for gen tests / gen build: "wbtest" (default) or "test"
  "test_kind": "wbtest"
}

world (string)

The World type name used during code generation. This is the struct that holds scenario state and is passed to every step handler.

Required for moonspec gen tests. Can be set here or via the --world CLI flag.

mode (string | object)

Controls how test functions are generated from Gherkin features. Two values are supported:

  • "per-scenario" (default) -- generates one test block per scenario. Each scenario runs independently with its own World instance.

  • "per-feature" -- generates one test block per feature file. All scenarios in the feature share a single test entry point.

The mode field also accepts a per-file map for mixed-mode projects:

{
  "mode": {
    "features/checkout.feature": "per-feature",
    "features/search.feature": "per-scenario",
    "*": "per-scenario"  // fallback for all other files
  }
}

The "*" key acts as a fallback. If a feature file path is not listed and no "*" key exists, the default "per-scenario" mode is used.

skip_tags (array of strings)

Gherkin tags that cause scenarios to be skipped during execution. Scenarios tagged with any of these tags are skipped without executing steps or hooks.

Tags may include a reason using the @skip("reason") syntax. The reason is extracted and attached to the skip status in results.

When omitted from config, the runtime default is ["@skip", "@ignore"].

moonspec gen tests uses the same tags. It puts a #skip("reason") attribute above each generated test whose scenario, feature or Examples block has a skip tag, so moon test reports the scenario as skipped. moon test --include-skipped runs it anyway. In per-feature mode, only a feature-level skip tag adds #skip.

no_color (boolean)

When true, moonspec gen tests generates tests that call options.no_color(true) and create pretty formatters with no_color=true. Default: false. Environment variables can override it at runtime; see Colors.

test_kind (string)

The kind of test file that moonspec gen tests and moonspec gen build write.

  • "wbtest" (default) -- white-box tests, <name>_feature_wbtest.mbt. They can use private items of the package, such as the World impl that moonspec gen steps generates.

  • "test" -- black-box tests, <name>_feature_test.mbt. They see only the package's public API, so the World type and its impl must be pub.

An -o value that ends in .mbt is the exact file name, whatever the kind.

steps (object)

Configuration for the moonspec gen steps command.

FieldTypeDefaultDescription
outputstring"alongside"Output strategy for generated step registration files
excludearray of string(none)Glob patterns for files to exclude from scanning

The output field accepts:

  • "alongside" (default) -- writes <world>_steps_gen.mbt next to the step functions.

  • "per-package" -- writes one moonspec_gen.mbt per MoonBit package.

  • "generated" -- writes to _generated/<world>_steps.mbt at the module root. That directory is not part of the World type's package, and MoonBit allows the World impl only there, so this output does not compile. gen steps prints a warning.

  • Custom path -- any other string is treated as a directory path. It must be the directory of the World type's package.

formatters (array of objects)

Configure output formatters for test results. Each entry specifies a formatter type and its output destination.

{
  "formatters": [
    { "type": "pretty", "output": "stdout" },
    { "type": "junit", "output": "reports/results.xml" },
    { "type": "messages", "output": "stderr" }
  ]
}

Each formatter entry has:

  • type -- "pretty", "junit", or "messages"

  • output -- "stdout", "stderr", or a file path

  • no_color -- (optional, pretty only) disable ANSI colors

Default behavior: When no formatters are configured, moonspec outputs pretty-formatted results to stdout.

Multiple formatters: You can configure multiple formatters simultaneously -- for example, pretty output to the console for local development and JUnit XML to a file for CI.

Programmatic API:

let fmt = @format.PrettyFormatter::new()
options.add_formatter(&fmt, @moonspec.Stdout)

let junit = @format.JUnitFormatter::new()
options.add_formatter(&junit, @moonspec.File("reports/results.xml"))

// Remove all formatters
options.clear_sinks()

Example: Minimal Config

{
  "world": "CalcWorld"
}

Example: Full Config

{
  "world": "EcomWorld",
  "mode": {
    "features/checkout.feature": "per-feature",
    "*": "per-scenario"
  },
  "skip_tags": ["@skip", "@ignore", "@wip"],
  "steps": {
    "output": "alongside",
    "exclude": ["vendor/*"]
  }
}

RunOptions (Runtime Configuration)

RunOptions controls how the runner executes features at runtime. It is created in your test code and passed to the runner.

let opts = RunOptions::new(features)
opts.parallel(true)
opts.max_concurrent(8)
opts.retries(2)
opts.tag_expr("@smoke and not @slow")
opts.skip_tags(["@skip", "@ignore", "@wip"])
opts.dry_run(false)

Builder Methods

parallel(value : Bool)

Enable or disable parallel scenario execution. Default: false.

When enabled, scenarios run concurrently up to the max_concurrent limit. Each scenario still gets its own World instance.

max_concurrent(value : Int)

Set the maximum number of scenarios that can run concurrently during parallel execution. Default: 4.

Only takes effect when parallel is true.

retries(value : Int)

Set the global retry count for failed scenarios. Default: 0.

When a scenario fails, it is re-executed up to value additional times. A fresh World instance is created for each attempt. Only the final attempt's result counts toward the run summary.

Per-scenario @retry(N) tags override this global setting. Negative values are clamped to 0.

tag_expr(value : String)

Set a boolean tag expression to filter which scenarios run. Default: "" (no filter).

Supports boolean operators:

"@smoke and not @slow"
"@checkout or @cart"
"not @wip"

scenario_name(value : String)

Filter scenarios by name. Default: "" (no filter).

Only scenarios whose name contains the given string will run.

dry_run(value : Bool)

Enable or disable dry-run mode. Default: false.

When enabled, steps are matched against definitions but handlers are not executed. All hooks are skipped. Matched steps report as Skipped("dry run") and undefined steps remain Undefined. Useful for validating step wiring without side effects.

skip_tags(tags : Array[String])

Set the tags that cause scenarios to be skipped. Default: ["@skip", "@ignore"].

Replaces the entire skip tag list. Scenarios with any of these tags are skipped without executing steps or hooks.

no_color(value : Bool)

Turn off ANSI colors in the run_or_fail failure report. Default: false. Environment variables can override it at runtime; see Colors.

add_sink(sink : &MessageSink)

Add a message sink for envelope output. Sinks receive structured messages as the run progresses. Multiple sinks can be added.

add_formatter(sink : &MessageSink, dest : OutputDest)

Register a formatter with an output destination. The formatter receives messages during the run, and its output is automatically written to the destination when the run completes.

Destinations: @moonspec.Stdout, @moonspec.Stderr, @moonspec.File(path).

clear_sinks()

Remove all registered sinks and formatters.


CLI Flags

CLI flags override the corresponding config file fields for the current invocation.

moonspec gen tests

FlagShortOverrides ConfigDescription
--world-wworldWorld type name (e.g. CalcWorld)
--mode-mmodeCodegen mode: per-scenario or per-feature
--config-c(all)Explicit config file path
--output-dir-o--Output directory for generated test files
--no-colorno_colorGenerate tests without ANSI colors
--test-kindtest_kindwbtest (default) or test

When --world or --mode is provided, the CLI value takes precedence over the config file value. When --config is provided, auto-discovery is bypassed entirely and only the specified file is loaded.

Note: --mode on the CLI applies uniformly to all files in that invocation. To use per-file mode mapping, configure it in the config file instead.

moonspec gen steps

FlagShortDescription
--config-cExplicit config file path
--dir-dDirectory to scan for step definitions

moonspec gen build

FlagShortOverrides ConfigDescription
--world-wworldWorld type name (e.g. CalcWorld)
--package-p--Package directory that gets the tests (default: src)
--config-c(all)Explicit config file path
--apply--Write missing entries to <package>/moon.pkg
--test-kindtest_kindwbtest (default) or test
--moonx--Rule runs moonx moonrockz/moonspec/cmd/moonspec@<CLI version> instead of moonspec

moonspec check

The check subcommand accepts positional .feature file arguments and does not use config file settings.


Colors

The run_or_fail failure report and PrettyFormatter use ANSI colors. Turn them off when you build the tests:

  • moonspec.json5: "no_color": true

  • CLI: moonspec gen tests --no-color

  • Code: options.no_color(true) or @format.PrettyFormatter::new(no_color=true)

Environment variables override the compiled value when the tests run:

VariableEffect
MOONSPEC_COLOR=alwaysColor on, even when no_color is set
MOONSPEC_COLOR=neverColor off
NO_COLOR=<any non-empty value>Color off, unless MOONSPEC_COLOR=always (no-color.org)

Editor test panels that read moon test --test-failure-json may show ANSI codes as raw text. Set NO_COLOR=1 for those runs.

Precedence Order

Configuration values are resolved with the following precedence (highest wins):

  1. CLI flags -- --world, --mode, etc.

  2. Override config -- moonspec.json5 (during auto-discovery)

  3. Base config -- moonspec.json (during auto-discovery)

  4. Built-in defaults -- mode: "per-scenario", skip_tags: ["@skip", "@ignore"], etc.

When --config is used, steps 2 and 3 are replaced by the single specified file.


Merge Behavior

When both moonspec.json and moonspec.json5 exist, they are merged using field-level override semantics:

  • Each top-level field in the override config (moonspec.json5) replaces the corresponding field in the base config (moonspec.json) when present.

  • Fields present in the base but absent from the override are preserved.

  • Fields present in the override but absent from the base are added.

  • For nested objects like steps, the entire object is replaced -- individual sub-fields are not merged independently.

  • For array fields like skip_tags, the override array replaces the base array entirely (arrays are not concatenated).

Example

moonspec.json (base):

{
  "world": "AppWorld",
  "mode": "per-scenario",
  "skip_tags": ["@skip", "@ignore"]
}

moonspec.json5 (override):

{
  "mode": "per-feature",
  "skip_tags": ["@skip", "@ignore", "@wip"]
}

Resolved config:

{
  "world": "AppWorld",           // preserved from base
  "mode": "per-feature",         // replaced by override
  "skip_tags": ["@skip", "@ignore", "@wip"]  // replaced by override
}

Schema

A JSON Schema is available at schemas/moonspec.schema.yaml in the moonspec repository. Use it for IDE validation and autocompletion in editors that support YAML/JSON schema references.

For VS Code, you can add a reference in your workspace settings:

{
  "json.schemas": [
    {
      "fileMatch": ["moonspec.json", "moonspec.json5"],
      "url": "./schemas/moonspec.schema.yaml"
    }
  ]
}
Edit this page on GitHub ↗