Skip to content

3 AL/BC patterns from CURABIS's internal automated-testing training material - #158

Open
Michael Dieringer (MichaelDieringer) wants to merge 3 commits into
microsoft:mainfrom
Curabis:community-contribution/academy-book-patterns
Open

Michael Dieringer (MichaelDieringer) wants to merge 3 commits into
microsoft:mainfrom
Curabis:community-contribution/academy-book-patterns

Conversation

@MichaelDieringer

@MichaelDieringer Michael Dieringer (MichaelDieringer) commented Sep 4, 2026

Copy link
Copy Markdown

Summary

Third batch from CURABIS ApS, mined from CURABIS's own internal Academy course book on automated/BDD testing in Business Central. Same format as #156/#157 (6-key frontmatter, Description/Best Practice/Anti Pattern, sibling .good.al/.bad.al samples, no fenced code in the .md).

  • data-modeling (1): after posting a sales order with Ship and Invoice together, the resulting Item Ledger Entry carries the shipment's document number (Sales Header."Last Shipping No."), not the posted invoice number — a lookup filtered on the invoice number silently matches zero rows. Sales-specific; purchase combined posting (Receive+Invoice) uses different fields.
  • testing (3): verify a field's actual rendered UI state with TestPage field .Visible()/.Enabled(), and its editability specifically with .Editable() opened via OpenEdit() — these are distinct states requiring different verification. Generate test fixture values via the right LibraryUtility helper for the guarantee actually needed: GenerateRandomCodeWithLength/GenerateRandomCode20 when uniqueness against the real table must be verified, GenerateGUID() (untruncated, full-length fields only) or GenerateRandomCode/GenerateRandomXMLText for incidental values — not hardcoded literals or a truncated GUID.

Most of the source book turned out to be BDD philosophy and CURABIS process content that doesn't clear BCQuality's admission test — these 4 are the genuinely atomic, non-obvious AL-specific patterns that survived a full read-through, checked against the current microsoft/knowledge/ corpus and CURABIS's own two prior PRs for overlap.

Test plan

  • CI frontmatter/format validation passes
  • Domain-owning reviewers confirm placement and accuracy per file

🤖 Generated with Claude Code

Third batch from CURABIS ApS: item-ledger-entry document-no lookup after Ship-and-Invoice posting, TestPage.Visible()/.Enabled() as the mechanism for verifying field UI state, and LibraryUtility.GenerateGUID() for collision-free test fixture values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Item Ledger Entry pattern checks out against current BCApps source: combined Ship+Invoice posting stamps the item journal line with the posted shipment number, while LibrarySales.PostSalesDocument returns Last Posting No. because the invoice branch overwrites its return-field selection.

I found two accuracy blockers in the testing guidance (called out inline) and one consumption gap: the current al-testing-review worklist has no GenerateGUID/CopyStr or TestPage Visible/Enabled/Editable cues or targeted checks, and al-data-modeling-review scopes relevance to setup/master-table surfaces without any Item Ledger Entry/posting cue. Please wire all three articles into their corresponding review-skill worklists so agents can actually select them; coordinate this with #155, whose contracts state that targeted checks cover every current article.

The CLA is also still pending. Because the PR says the material was mined from CURABIS's internal Academy course book, the contributor needs to use the applicable company/employer declaration, confirm authorization to contribute the derived material, and make any third-party-material disclosure required by the CLA.

Comment thread microsoft/knowledge/testing/use-generateguid-for-unique-test-fixture-values.md Outdated
@MichaelDieringer

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree [company="CURABIS ApS"]

@MichaelDieringer

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="CURABIS ApS"

- use-generateguid-for-unique-test-fixture-values.md: GenerateGUID()
  is a Code[10] number-series value, not a real GUID; truncating it
  with CopyStr for a shorter field cuts off the changing digits. Point
  to GenerateRandomCode/GenerateRandomCodeWithLength/GenerateRandomXMLText
  instead, which verify uniqueness against the actual table.
- Split use-testpage-visible-enabled-to-verify-field-ui-state.md: drop
  its editability claim (the sample opens with OpenView() and asserts
  Enabled(), which verifies enabled state, not editability — Editable()
  and Enabled() are distinct TestField methods). New companion article
  use-testpage-editable-to-verify-field-editability.md covers Editable()
  with OpenEdit() specifically.
- Wire GenerateGUID/CopyStr and TestPage Visible/Enabled/Editable cues
  into al-testing-review.md, and the Item Ledger Entry/Last Shipping No.
  posting cue into al-data-modeling-review.md.

The Item Ledger Entry article itself was independently verified against
current BCApps source and needs no changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up after ab4ed50: the Visible/Enabled scope and the Editable/OpenEdit distinction now look sound, and the Item Ledger Entry article is reasonable for combined sales Ship+Invoice posting. The three remaining substantive issues are inline.

Please also update the PR description: it still advertises the superseded GenerateGUID() + CopyStr recommendation and no longer describes the current change.

Comment thread microsoft/knowledge/testing/use-generateguid-for-unique-test-fixture-values.md Outdated
Comment thread microsoft/skills/review/al-testing-review.md Outdated
Comment thread microsoft/skills/review/al-data-modeling-review.md Outdated


- use-generateguid-for-unique-test-fixture-values.md/.good.al: documented
  each LibraryUtility helper's actual behavior, verified against
  LibraryUtility.Codeunit.al. GenerateRandomCode opens the target table as
  a temporary RecordRef, so despite taking TableNo it never checks real
  data. GenerateRandomXMLText performs no table lookup at all. Only
  GenerateRandomCodeWithLength/GenerateRandomCode20 (capped at Code[10]/
  Code[20]) genuinely verify against the real table. Fixture switched to
  GenerateRandomCodeWithLength where the comment claims verified
  uniqueness.
- al-testing-review.md: rewired the cue to catch the actual anti-pattern
  (hardcoded literals, hand-built uniqueness, short-field GUID truncation)
  instead of only matching the compliant GenerateGUID()+CopyStr shape;
  broadened tokens to include TestPage, Library - Utility, and
  .Visible()/.Enabled()/.Editable().
- al-data-modeling-review.md: restricted the Item Ledger Entry
  Last-Shipping-No. cue to sales combined posting; purchase combined
  posting is Receive+Invoice and uses different fields entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@MichaelDieringer

Copy link
Copy Markdown
Author

Jesper Schulz-Wedde (@JesperSchulz) Addressed in 1d05811 (pushed 2026-09-08): the 4 inline issues above, plus the PR description — updated to describe the actual current recommendation (pick the LibraryUtility helper by the guarantee needed) instead of the superseded blanket GenerateGUID()+CopyStr line. Ready for another look whenever you have time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you—the helper behavior, fixture implementation, review relevance, sales-only posting scope, and PR description are all much improved. This PR is very close.

Two factual points remain in the uniqueness article. We recognize that another round is inconvenient, but these claims directly determine which test code agents will flag and which helper guarantees they will trust, so they need to be precise. We have limited the feedback to those two points and omitted optional cleanup.


## Description

A fixture helper that assigns a hardcoded literal to a primary-key or descriptive field collides the moment two tests, or two runs of the same test, create that fixture without cleanup, and a literal longer than the field allows raises a truncation or insert error. `LibraryUtility.GenerateGUID()` is not a real GUID — it is a `Code[10]` number-series value (`GU00000000`–`GU99999999`) — and it returns the full 10 characters unshortened. Truncating it yourself with `CopyStr(..., 1, MaxStrLen(ShorterField))` for a field under 10 characters is unsafe: the changing digits sit at the right end and are exactly what gets cut off, so consecutive calls into a short field can produce the same truncated value. `GenerateGUID()` is only safe as-is for a field that holds the full 10 characters.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restrict the collision rule to primary/unique-key fields or values the test explicitly relies on as unique lookup identifiers. An ordinary descriptive field is not unique, so two rows with the same description do not collide on insert; deterministic descriptive text is also often required for exact assertions. The anti-pattern at line 31 and the testing review cue should be narrowed consistently so agents do not flag valid descriptive test data.


For a field that holds the full 10 characters, assign `LibraryUtility.GenerateGUID()` directly. For a shorter field, do not truncate a GUID yourself — but also do not assume every `LibraryUtility` helper verifies uniqueness against the real table, because they don't all behave the same way:

- `GenerateRandomCode(FieldNo, TableNo)` opens the target table as a **temporary** `RecordRef`, so its own emptiness check never inspects real rows — despite taking `TableNo`, it does not verify against the actual table. It's safe to use for its non-colliding-*within-a-single-test-run* value (derived from `GenerateGUID()`'s own number series), not for a guarantee against pre-existing or leftover data.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GenerateRandomCode cannot guarantee non-collision even within one test run for short fields. It takes the rightmost FieldRef.Length characters of the sequential GenerateGUID() value but does not remember earlier temporary results; a Code[1] value repeats every 10 calls, Code[2] every 100, and so on. Please remove the within-run guarantee and describe it consistently with line 25 as a finite short-field namespace with only a low collision chance. Keep the real-table helpers for cases that require verified uniqueness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants