Golden-file testing pins the rendered (and, optionally, checked) output of a ledger so that any unintended change is caught as a regression.
Layout
Fixtures live under test/fixtures/golden/. Each case is a directory:
test/fixtures/golden/
salary/
input.exs # Elixir script whose last expression is a directive list
expected.bean # expected rendered Beancount text
expected.result.json # expected normalized result (requires bean-check)input.exs
[
Beancount.open(~D[2026-01-01], "Assets:Bank", ["USD"]),
Beancount.open(~D[2026-01-01], "Income:Salary", ["USD"]),
Beancount.transaction(~D[2026-01-31], "*", "Employer", "Salary", [
Beancount.posting("Assets:Bank", Decimal.new("5000"), "USD"),
Beancount.posting("Income:Salary", Decimal.new("-5000"), "USD")
])
]How the tests work
Beancount.GoldenTest iterates every case from Beancount.Golden.cases/0 and
asserts that rendering input.exs equals expected.bean. Because rendering is
deterministic, this needs no Beancount installation and runs as part of the
default mix test.
The expected.result.json comparison lives in Beancount.IntegrationTest and
is tagged :beancount, so it only runs when you opt in.
Regenerating
mix beancount.golden.update
The task regenerates every expected.bean from its input.exs. When
bean-check is available it also regenerates expected.result.json; otherwise
that step is skipped. Re-running with unchanged inputs produces no diff.
Adding a case
- Create
test/fixtures/golden/<name>/input.exs. - Run
mix beancount.golden.update. - Review and commit the generated files.
Turbobean-derived cases
Many fixtures under test/fixtures/golden/ were ported from
turbobean/tests/golden
as input.exs directive lists (booking, pad, balance assertions, etc.). The
pnl case was skipped because it uses a turbobean-specific directive.
To regenerate ported input.exs definitions from the embedded script:
mix run scripts/generate_turbobean_golden_inputs.exs
mix beancount.golden.update