mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
* Add 4 more AL/BC testing patterns from Luc van Vugt's fluxxus.nl blog Fourth batch from CURABIS ApS, mined from an external BC/NAV testing expert's blog archive (fluxxus.nl). Confirm+StrSubstNo interaction with ConfirmHandler, Table Relation Test's OnAfterRemoveTableRelation exclusion hook (verified against BCApps source, codeunit 134926), committing shared lazy-Initialize fixture data, and Assert.IsFalse vs asserterror for boolean checks. * Address Jesper Schulz-Wedde's review on PR #159 - transactionmodel-attribute-governs-test-transactions.md: the "Commit causes an error" behavior is specific to an explicitly declared AutoRollback attribute. A test method with no TransactionModel attribute at all is a distinct, valid shape — BCApps' own codeunit 134915 "ERM Online Mapping Setup" commits inside a lazy Initialize() with no attribute declared, cleaning up via a manual asserterror at the end. Evidence for commit-shared-test-fixture- inside-lazy-initialize.md (this PR), which is correct as submitted. - confirm-needs-strsubstno-before-confirmhandler-sees-substituted-text.md: reframe as a known, unconfirmed-fix platform defect (microsoft/ALAppExtensions#23935) rather than designed behavior; add the Message/MessageHandler asymmetry as supporting evidence. - table-relation-test-exclude-known-invalid-relations-via-event.md: note the test-app-only consumer dependency; correct "walks every TableRelation field property in the app" to the actual tenant-wide Table Relations Metadata scope across installed apps. - Wire confirm-needs-strsubstno, commit-shared-test-fixture-inside- lazy-initialize, and table-relation-test-exclude-known-invalid- relations-via-event into al-testing-review.md's candidate-selection cues. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Address second round of Jesper Schulz-Wedde's review on PR #159 - 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> * Fix remaining correctness issues from Jesper's 2026-09-15 re-review - commit-shared-test-fixture-inside-lazy-initialize: three sub-issues. Recommended TestIsolation = Codeunit instead of listing Disabled as an equal option - Disabled never rolls back at all ("tests are not isolated from each other" per the property's own docs), so a fixture this pattern commits under Disabled is permanent database contamination unless something else tears it down; Disabled is now only mentioned alongside that explicit teardown requirement. Added precedence in al-testing-review.md so the deliberate end-of-test asserterror Error(...) rollback sentinel isn't also flagged by the generic asserterror-needs-expectederror-and-code rule. Rewrote both fixtures to actually demonstrate the pattern: persisted fixture data (an Item record) instead of an empty comment, a second [Test] method that depends on the fixture surviving into it, and an explicit Subtype = TestRunner / TestIsolation = Codeunit runner codeunit. - table-relation-test-exclude-known-invalid-relations-via-event: the length/type rule was stated as one global requirement. Verified ValidateFieldRelation in codeunit 134926 directly (BCApps reference clone) and split it into the two branches the source actually has: a field with any unconditional relation needs exact length and exact resolved type; a field whose relations are all conditional only fails on being shorter (longer is fine) than the largest related field, and when the required type is specifically Code, a Text source passes too - a tolerance that does not apply on the unconditional side and does not extend to a required Text. Rebased onto upstream/main (one conflict in transactionmodel-attribute-governs-test-transactions.md - upstream had already linked its sample references via the READ convention, ours added a Source section; merged both). Also converted the 3 remaining plain-backtick sample references in this PR to the READ-convention markdown-link form, same fix as #156/#157/#158. * Fix four merge-critical issues from Jesper's 2026-09-22 review - al-testing-review.md: the generic ExpectedError cue's asserterror Assert.IsTrue/IsFalse exclusion was unconditional, but the specialized rule it deferred to only claims the pure-inversion shape. A test expecting the guarded Boolean-returning call itself to raise fell through both routes. Narrowed the exclusion to the same inversion-only condition the specialized cue already uses. - asserterror-needs-expectederror-and-code.md: the rollback-sentinel exception (a trailing asserterror Error(...) used purely to force a fixture rollback, not to verify a specific failure) previously lived only in skill routing prose. Encoded it directly in the article's Anti Pattern section so every consumer of the knowledge base sees it, not just this one skill. - commit-shared-test-fixture-inside-lazy-initialize.good.al/.bad.al: replaced hand-rolled Item.Init()/Insert(true) with LibraryInventory.CreateItem, so the canonical fixture doesn't itself trigger use-library-codeunits-for-test-fixtures. - table-relation-test-exclude-known-invalid-relations-via-event.good.al/ .bad.al: declared minimal "Sample Setup"/"Sample Header" tables inline instead of referencing undefined symbols, matching this repo's own convention that every fixture is self-contained. * Give the table-relation-test fixtures a real relation to exclude and one to protect The "Category Code" field had no TableRelation at all, so the good subscriber's RemoveTableRelation call targeted metadata that never existed - a no-op. Added a real TableRelation to "Sample Setup" on that field (the one known exception to exclude) and a second, ordinary self-referencing relation ("Parent No." -> "Sample Header"."No.") with no exception. The good fixture now removes only the first; the bad fixture's table-wide removal (field/related table/field all 0) now demonstrably also strips the second, showing the actual anti-pattern instead of removing nothing meaningful. --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
35 lines
3.5 KiB
Markdown
35 lines
3.5 KiB
Markdown
---
|
|
bc-version: [all]
|
|
domain: testing
|
|
keywords: [table-relation-test, tablerelationsmetadata, onafterremovetablerelation, field-length, field-type]
|
|
technologies: [al]
|
|
countries: [w1]
|
|
application-area: [all]
|
|
---
|
|
|
|
# Exclude a known-valid TableRelation exception via OnAfterRemoveTableRelation
|
|
|
|
## Description
|
|
|
|
Codeunit 134926 "Table Relation Test" (shipped in BCApps' test app — only consumers that depend on the BC test libraries can subscribe to it) reads Table Relations Metadata tenant-wide across every installed app, not just the current one, and validates each field's type and length against what its relations require — but the exact rule depends on whether that field has an *unconditional* relation (a `Table Relations Metadata` row with `Condition Field No. = 0`) among its relations, or only *conditional* ones:
|
|
|
|
- If any relation is unconditional, the field's length must equal *exactly* the largest related field's length, and its type must exactly match the required type — resolved to `Text` when the related fields themselves mix `Code` and `Text`.
|
|
- If every relation for that field is conditional, the requirement relaxes: the field only needs to be *at least* as long as the largest related field (longer is accepted; only shorter fails), and when the required type is specifically `Code`, both a `Code` and a `Text` source field pass. That `Code`/`Text` tolerance is conditional-only — it does not apply on the unconditional side, and it does not extend to a required type of `Text` (a `Code` source field does not satisfy a required `Text`).
|
|
|
|
A field with a legitimate, intentional relation shape outside both of these tolerances has no per-field override in its own object definition; the check runs with no built-in escape hatch beyond them. The validation test method itself is `[Scope('OnPrem')]`: it only runs from an on-premises test surface, not from a cloud-targeted test app, so this whole exception mechanism — and the check it works around — is only reachable where that test can actually execute.
|
|
|
|
## Best Practice
|
|
|
|
Subscribe to `OnAfterRemoveTableRelation` and call the codeunit's own `RemoveTableRelation(TableRelationsMetadata, TableID, FieldID, RelatedTableID, RelatedFieldID)` to strike the one known-valid relation before the test evaluates it, scoped as narrowly as the exception actually is. Because the test itself is `[Scope('OnPrem')]`, do not recommend subscribing to it as a way to guard a cloud-targeted app's test suite — the subscription has no effect where the test never runs.
|
|
|
|
See sample: [`table-relation-test-exclude-known-invalid-relations-via-event.good.al`](table-relation-test-exclude-known-invalid-relations-via-event.good.al).
|
|
|
|
## Anti Pattern
|
|
|
|
Excluding an entire table's relations (or disabling the whole test codeunit) to work around one known exception. This discards the check's coverage for every other relation on that table, or in the app, not just the one that needed an exception.
|
|
|
|
See sample: [`table-relation-test-exclude-known-invalid-relations-via-event.bad.al`](table-relation-test-exclude-known-invalid-relations-via-event.bad.al).
|
|
|
|
## Source
|
|
|
|
The `OnAfterRemoveTableRelation` exclusion technique is drawn from Luc van Vugt's "How-to: Test your Table Relations (2)": https://www.fluxxus.nl/index.php/bc/how-to-test-your-table-relations-2/. The codeunit/event signature, the `[Scope('OnPrem')]` boundary, and the tenant-wide `Table Relations Metadata` scope described above were verified directly against BCApps' `codeunit 134926 "Table Relation Test"` source, not taken from the post.
|