N NezamDocumentation

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#

bl
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.

MemberShape
Contextcontext name: Type = expression;
Assertionassert expression;
Expectationexpect expression to matcher;
Setupsetup { ... }
Teardownteardown { ... }

Context values#

Context values define typed inputs for the test. The initializer is optional when the test runtime supplies the value.

bl
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.

bl
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.

bl
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.

bl
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.

bl
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:

PathExample expectation
Configured successexpect command(input) to be expected_result;
Missing configurationexpect command(input) to throw MissingConfigError;
Ambiguous configurationexpect command(input) to throw AmbiguousConfigError;
Configured denialexpect 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.

Source: packages/business/language/test-declarations.md