- 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>
2.6 KiB
| bc-version | domain | keywords | technologies | countries | application-area | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
testing |
|
|
|
|
Use Assert.IsFalse to check a boolean result, not asserterror around Assert.IsTrue
Description
asserterror exists to assert that a statement raises a runtime error; it is not a general-purpose way to invert a boolean check. Wrapping asserterror Assert.IsTrue(SomeFunc(), Msg) to verify that SomeFunc() returns false tests whether Assert.IsTrue's own error-raising behavior fired, not the value SomeFunc() actually returned.
Best Practice
When the code under test returns a Boolean rather than raising an error, assert the value directly with Assert.IsFalse(SomeFunc(), Msg) (or Assert.IsTrue for the positive case). Reserve asserterror for statements expected to actually raise an error.
See sample: use-assert-isfalse-not-asserterror-for-boolean-checks.good.al.
Anti Pattern
asserterror Assert.IsTrue(SomeFunc(), Msg); to verify SomeFunc() is false. It passes today because Assert.IsTrue happens to raise an error on failure, but it verifies the assertion helper's error-raising behavior, not the value under test.
See sample: use-assert-isfalse-not-asserterror-for-boolean-checks.bad.al.
Source
Drawn from Luc van Vugt's "TDD in NAV – ASSERTERROR or IsFalse": https://www.fluxxus.nl/index.php/bc/tdd-in-nav-asserterror-or-isfalse/. The post's own example and reasoning — reserve asserterror for the product code actually raising an error, use Assert.IsFalse/Assert.IsTrue to check a boolean the test framework itself computes — carries over directly; the overlap with asserterror-needs-expectederror-and-code.md below is this repository's own addition, not from the source.
Scope
This rule and asserterror-needs-expectederror-and-code.md can both match asserterror Assert.IsTrue(SomeFunc(), Msg); with nothing after it — the generic rule sees a bare asserterror, this one sees asserterror wrapping an Assert.IsTrue/Assert.IsFalse call used to invert a boolean. This rule wins for that shape: the fix is to replace the construct with a direct Assert.IsFalse/Assert.IsTrue call, not to add Assert.ExpectedError/Assert.ExpectedErrorCode after it. asserterror-needs-expectederror-and-code.md still applies on its own to every other bare asserterror, including one guarding Assert.IsTrue/Assert.IsFalse where the intent genuinely is to assert that the guarded call itself raises an error (for example, asserting that a validation helper errors before it can even return a boolean).