One run, every format.
Behave's runner is decoupled from its output. Every line it prints is emitted
through a formatter. Switch with --format NAME. The same spec, rendered
for humans, for CI, for tools, or for the web.
The spec we'll run
describe 'Calculator', {
context 'addition', {
it 'adds positive numbers', { expect(1 + 1).to.be(2) }
it 'overflows on MAX_INT', { expect(2147483647 + 1).to.be(-2147483648) }
pending 'handles negative-zero', 'not yet implemented', { }
}
context 'subtraction', {
it 'subtracts a positive', { expect(5 - 3).to.be(2) }
}
}
The classic indented tree. One line per describe/context/it,
with a colored SUCCESS / FAILURE marker per example. Useful for
debugging: you can see exactly which example is running when something hangs.
⮑ 'Calculator'
⮑ 'addition'
⮑ 'adds positive numbers'
⮑ SUCCESS
⮑ 'overflows on MAX_INT'
⮑ FAILURE
⮑ 'handles negative-zero'
⮑ PENDING
⮑ 'subtraction'
⮑ 'subtracts a positive'
⮑ SUCCESS
Failures:
[ ✗ ] specs/calc-spec.raku:5
Expected: 2147483648
to be: -2147483648
4 examples, 1 failed, 1 pending, 2 passed
The default. One character per example: . pass, F fail,
* pending, S skipped. Failures and counts print after the
stream completes.
.F*.
Failures:
[ ✗ ] specs/calc-spec.raku:5
Expected: 2147483648
to be: -2147483648
4 examples, 1 failed, 1 pending, 2 passed
A clean, document-style rendering of the spec tree. Each group is a heading,
each example a line beneath it. Outcome suffixes ((FAILED),
(PENDING)) annotate non-passing examples.
Calculator
addition
adds positive numbers
overflows on MAX_INT (FAILED)
handles negative-zero (PENDING)
subtraction
subtracts a positive
Failures:
[ ✗ ] specs/calc-spec.raku:5
Expected: 2147483648
to be: -2147483648
4 examples, 1 failed, 1 pending, 2 passed
A self-contained HTML5 report with collapsible describe/context
nesting using native <details>, no JavaScript required. Inline styles, no
external CSS. Redirect to a file and open it.
4 examples, 1 failed, 1 pending, 2 passed
Calculator
addition
specs/calc-spec.raku:5 Expected: 2147483648 to be: -2147483648
subtraction
↑ Rendered preview of the HTML formatter's output. Open the real
report.html and you'll see this with click-to-collapse working out of
the box.
A single JSON document emitted on stdout after the run finishes. Designed for CI dashboards and tool integration. Stable, versioned schema.
{
"version": 1,
"order": "random",
"seed": 42,
"aborted": false,
"examples": [
{
"description": "adds positive numbers",
"full_description": "Calculator addition adds positive numbers",
"status": "passed",
"file": "specs/calc-spec.raku",
"line": 3,
"duration": 0.001,
"tags": []
},
{
"description": "overflows on MAX_INT",
"full_description": "Calculator addition overflows on MAX_INT",
"status": "failed",
"file": "specs/calc-spec.raku",
"line": 5,
"duration": 0.001,
"failure": {
"expectations": [
{
"file": "specs/calc-spec.raku",
"line": 5,
"given": 2147483648,
"expected": -2147483648,
"negated": false
}
]
}
},
{
"description": "handles negative-zero",
"status": "pending",
"pending_reason": "not yet implemented",
"file": "specs/calc-spec.raku",
"line": 6
},
{
"description": "subtracts a positive",
"status": "passed",
"file": "specs/calc-spec.raku",
"line": 10,
"duration": 0.001
}
],
"summary": {
"total": 4,
"passed": 2,
"failed": 1,
"pending": 1,
"skipped": 0,
"duration": 0.045
},
"summary_line": "4 examples, 1 failed, 1 pending, 2 passed"
}
JUnit XML, consumable by Jenkins, GitLab CI, CircleCI, and every dashboard that
ingests the testsuites / testsuite / testcase
schema. The describe/context chain joins into classname for hierarchical grouping.
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="behave" tests="4" failures="1" errors="0" skipped="1" time="0.045">
<testsuite name="calc-spec.raku" tests="4" failures="1" errors="0" skipped="1" time="0.045">
<testcase classname="Calculator > addition" name="adds positive numbers"
file="specs/calc-spec.raku" line="3" time="0.001"/>
<testcase classname="Calculator > addition" name="overflows on MAX_INT"
file="specs/calc-spec.raku" line="5" time="0.001">
<failure type="Expectation" message="Expected 2147483648 to be -2147483648"><![CDATA[
specs/calc-spec.raku:5
Expected: 2147483648
to be: -2147483648
]]></failure>
</testcase>
<testcase classname="Calculator > addition" name="handles negative-zero"
file="specs/calc-spec.raku" line="6" time="0">
<skipped message="pending: not yet implemented"/>
</testcase>
<testcase classname="Calculator > subtraction" name="subtracts a positive"
file="specs/calc-spec.raku" line="10" time="0.001"/>
</testsuite>
</testsuites>
Test Anything Protocol v13. Compatible with prove, tappy,
and every TAP consumer. # TODO for pending, # SKIP for
skipped. Pending examples are expected fails, not failures.
TAP version 13
1..4
ok 1 - Calculator addition adds positive numbers
not ok 2 - Calculator addition overflows on MAX_INT
---
severity: 'fail'
file: 'specs/calc-spec.raku'
line: 5
message: 'Expected 2147483648 to be -2147483648'
got: '2147483648'
expected: '-2147483648'
...
ok 3 - Calculator addition handles negative-zero # TODO not yet implemented
ok 4 - Calculator subtraction subtracts a positive
#write your own
Every formatter is a Raku class that composes the BDD::Behave::Formatter
role. The role declares a hook for every interesting event the runner emits.
All default to no-ops, so a custom formatter only overrides what it cares about.
use BDD::Behave::Formatter;
use BDD::Behave::Formatter::Registry;
class DotsFormatter does BDD::Behave::Formatter {
method name(--> Str) { 'dots' }
method example-pass($example) { print '.' }
method example-fail($example, :$failure-info) { print 'F' }
method example-pending($example) { print '*' }
method example-skipped($example) { print 'S' }
method run-summary($result, *%) {
say "";
say "{$result.total} examples, {$result.failed} failed, {$result.passed} passed";
}
}
BDD::Behave::Formatter::Registry.register('dots', DotsFormatter);
Once registered, the formatter is selectable like any built-in:
behave --format dots specs/
suite-start, group-start,
example-pass/fail/pending/skipped,
run-summary, and multi-file-overall are the most common.
See the docs for the full lifecycle.