Test declarations#
Test declarations describe executable checks for Business Language behavior. Use them to cover resolver success paths, closed failure paths, command behavior, validation rules, and message-backed errors.
Declaration shape#
test PostingPolicyResolution {
context invoice: Invoice = sample_invoice;
context posting_date: date = today();
setup {
seed_posting_policy(invoice.company_code, invoice.document_type);
}
assert invoice.amount > 0;
expect post_invoice(invoice.invoice_id, posting_date) to be invoice;
teardown {
clear_posting_policy(invoice.company_code, invoice.document_type);
}
}A test has a name and a body. The body can contain context declarations, assertions, expectations, setup blocks, and teardown blocks.
| Member | Shape |
|---|---|
| Context | context name: Type = expression; |
| Assertion | assert expression; |
| Expectation | expect expression to matcher; |
| Setup | setup { ... } |
| Teardown | teardown { ... } |
Context values#
Context values define typed inputs for the test. The initializer is optional when the test runtime supplies the value.
test VendorContextTest {
context vendor_id: VendorId;
context company_code: CompanyCode = "1000";
assert company_code is not null;
}Use context values for inputs that make the test scenario explicit. Avoid hidden setup data for company, tenant, document type, or effective date when those values drive policy resolution.
Assertions#
Assertions are direct boolean checks.
test InvoiceAmountTest {
context invoice: Invoice = sample_invoice;
assert invoice.amount > 0;
assert invoice.currency_code is not null;
}Assertions are best for invariant checks inside the scenario. For command results and failure paths, use expectations.
Expectations#
Expectations compare an expression with a matcher.
test PostingExpectations {
context invoice: Invoice = sample_invoice;
expect invoice.status to be "draft";
expect invoice.tags to contain "payables";
expect calculate_total(invoice) to equal invoice.amount + invoice.tax_amount;
}Supported comparison matchers are be, equal, and contain.
Throw expectations#
Use throw expectations for closed failure paths.
test MissingPostingPolicyFailsClosed {
context invoice: Invoice = sample_invoice_without_policy;
context posting_date: date = today();
expect post_invoice(invoice.invoice_id, posting_date) to throw PostingPolicyError;
}Missing required configuration, unsupported modes, invalid actions, and ambiguous policies should be tested as failures. Do not write tests that expect silent fallback defaults for required policy.
Setup and teardown#
Setup and teardown blocks use standard block statements.
test PostingPolicyLifecycle {
context company_code: CompanyCode = "1000";
context document_type: DocumentType = "vendor_invoice";
setup {
seed_posting_policy(company_code, document_type);
}
expect resolve_posting_policy(company_code, document_type, today()) to be active_policy;
teardown {
clear_posting_policy(company_code, document_type);
}
}Keep setup deterministic. A test for tenant-specific policy should create the exact rows it needs and clear them afterward.
Configuration-driven coverage#
Every new configuration-driven rule should have at least these tests:
| Path | Example expectation |
|---|---|
| Configured success | expect command(input) to be expected_result; |
| Missing configuration | expect command(input) to throw MissingConfigError; |
| Ambiguous configuration | expect command(input) to throw AmbiguousConfigError; |
| Configured denial | expect command(input) to throw PolicyDeniedError; |
Use Configuration resolver patterns for the resolver shape and Messages and errors for declared user-facing failures.
Test boundary#
Tests should exercise the layer that owns the behavior. If a Business Language case fails, fix the grammar, compiler, runtime, or BL source layer that owns the issue. Do not add business-specific shims to shared language packages.