- commit-shared-test-fixture-inside-lazy-initialize.md: fundamentally
rewritten. AutoCommit is the documented default TransactionModel, not
AutoRollback. Explains the real mechanism (Commit() protects a fixture
from the test method's own later deliberate rollback, per Codeunit.Run/
TransactionModel-property semantics) and the TestIsolation dependency
(Disabled/Codeunit survive across methods, Function does not). Fixtures
rewritten to demonstrate the actual failure/success shape.
- transactionmodel-attribute-governs-test-transactions.md: now states the
AutoCommit default explicitly and agrees with the article above, closing
the contradiction Jesper flagged between the two testing articles.
- Deleted confirm-needs-strsubstno-before-confirmhandler-sees-substituted-text
(.md/.good.al/.bad.al): the underlying platform bug (microsoft/
ALAppExtensions#23935) was closed as completed in Feb 2024; cannot be
reproduced or bc-version-pinned on any currently supported version.
- table-relation-test-exclude-known-invalid-relations-via-event.md: added
the [Scope('OnPrem')] boundary verified against BCApps' Table Relation
Test codeunit.
- use-assert-isfalse-not-asserterror-for-boolean-checks.md: added a Scope
section resolving the overlap with asserterror-needs-expectederror-and-code.
- al-testing-review.md: fixed the shared-fixture cue to catch the actual
anti-pattern instead of the compliant shape, added the missing cue for
use-assert-isfalse-not-asserterror-for-boolean-checks, wired precedence
between it and the generic asserterror rule, and removed the cue for the
deleted article.
- Added in-file Source provenance (specific fluxxus.nl post per article,
with what was independently verified vs. taken from the post) to the
three surviving externally-inspired articles, per Jesper's request that
provenance live in the knowledge file itself, not only the PR description.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.6 KiB
| bc-version | domain | keywords | technologies | countries | application-area | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
testing |
|
|
|
|
Match TransactionModel to the commit behavior of the code under test
Description
[TransactionModel(...)] declares how a test method interacts with the database's write transaction. The attribute applies only to methods inside a codeunit with SubType = Test and takes one of three values: AutoRollback, AutoCommit, or None. AutoCommit is the documented default — a test method with no [TransactionModel(...)] attribute at all runs under AutoCommit, not AutoRollback and not None (Microsoft's TransactionModel property reference states this explicitly: "AutoCommit is the default value"). The "a call to Commit produces a runtime error" behavior is specific to the explicitly declared AutoRollback attribute. BCApps' own canonical pattern for a lazily-initialized shared fixture (see codeunit 134915 "ERM Online Mapping Setup") declares no TransactionModel attribute at all — so it runs under the AutoCommit default — calls Commit() inside its Initialize() helper, and cleans up manually with a deliberate asserterror Error(...) at the end rather than relying on automatic rollback; this is a legitimate, common pattern, not a bug. Per the same reference, under AutoCommit an error, even one caught by asserterror, still rolls back the transaction — but "only to the point at which Commit was called" if the code being tested committed first. When a test method does declare AutoRollback explicitly, the choice must match the code being exercised: per the platform reference, "if the code that you test includes calls to the COMMIT Method, then set the TransactionModel property on the test method to AutoCommit." Applying AutoRollback to a test that drives code which calls Commit produces a runtime error on the first Commit, not a meaningful assertion failure.
Best Practice
Leave [TransactionModel(...)] undeclared to get the AutoCommit default when the codeunit's own tests rely on that default's behavior — for example a lazily-initialized shared fixture that commits once and cleans up its own scratch changes with a manual asserterror-based rollback (see commit-shared-test-fixture-inside-lazy-initialize.md); do not treat that absence as equivalent to declaring AutoRollback. When declaring [TransactionModel(...)] explicitly instead, pick AutoRollback for a test whose own logic and the code it exercises make no Commit call, AutoCommit when the code under test genuinely calls Commit — posting routines, job-queue handlers, integration flows — and make the test exercise that commit path, and None for a read-only test or one that drives UI code without writing from the test method itself. Pair an intentional, suite-wide reliance on AutoCommit with a TestIsolation-enabled test runner so committed changes are reverted at a higher scope.
See sample: transactionmodel-attribute-governs-test-transactions.good.al.
Anti Pattern
Declaring [TransactionModel(AutoRollback)] explicitly on a test method without checking whether the tested business logic calls Commit. The test throws at the first Commit, leaving no verdict on the behavior it intended to verify; in a CI run this looks like a flake or a setup bug, not a specification mismatch. The mirror-image anti-pattern is defaulting to AutoCommit across the suite "to avoid the error" — without a TestIsolation runner this permanently dirties the test database between runs and produces order-dependent test outcomes. Flagging a Commit() call in a test method that declares no TransactionModel attribute at all is not this anti-pattern — that shape does not error, and is BCApps' own documented pattern for shared lazy fixtures.
See sample: transactionmodel-attribute-governs-test-transactions.bad.al.
Source
The AutoCommit-is-default claim and the exact rollback-to-last-Commit mechanics are quoted from Microsoft's TransactionModel Property reference: https://learn.microsoft.com/en-us/previous-versions/dynamicsnav-2018-developer/TransactionModel-Property. The current AL TransactionModel attribute page describes the same three values but never states a default; this older property reference is the citable source for that fact.