mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-07 15:46:55 +01:00
Add 3 more AL/BC patterns from CURABIS Academy testing course material
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.
This commit is contained in:
parent
07e324ddbc
commit
1028aacd4f
9 changed files with 155 additions and 0 deletions
|
|
@ -0,0 +1,12 @@
|
||||||
|
codeunit 50130 "Sample Item Ledger Lookup"
|
||||||
|
{
|
||||||
|
procedure GetPostedItemLedgerEntries(var SalesHeader: Record "Sales Header"; var ItemLedgerEntry: Record "Item Ledger Entry")
|
||||||
|
var
|
||||||
|
LibrarySales: Codeunit "Library - Sales";
|
||||||
|
InvoiceNo: Code[20];
|
||||||
|
begin
|
||||||
|
InvoiceNo := LibrarySales.PostSalesDocument(SalesHeader, true, true);
|
||||||
|
ItemLedgerEntry.SetRange("Document No.", InvoiceNo);
|
||||||
|
ItemLedgerEntry.FindSet();
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
codeunit 50130 "Sample Item Ledger Lookup"
|
||||||
|
{
|
||||||
|
procedure GetPostedItemLedgerEntries(var SalesHeader: Record "Sales Header"; var ItemLedgerEntry: Record "Item Ledger Entry")
|
||||||
|
var
|
||||||
|
LibrarySales: Codeunit "Library - Sales";
|
||||||
|
ShippingNo: Code[20];
|
||||||
|
begin
|
||||||
|
LibrarySales.PostSalesDocument(SalesHeader, true, true);
|
||||||
|
ShippingNo := SalesHeader."Last Shipping No.";
|
||||||
|
ItemLedgerEntry.SetRange("Document No.", ShippingNo);
|
||||||
|
ItemLedgerEntry.FindSet();
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
bc-version: [all]
|
||||||
|
domain: data-modeling
|
||||||
|
keywords: [item-ledger-entry, document-no, last-shipping-no, ship-and-invoice, posting, sales-order]
|
||||||
|
technologies: [al]
|
||||||
|
countries: [w1]
|
||||||
|
application-area: [all]
|
||||||
|
---
|
||||||
|
|
||||||
|
# After Ship-and-Invoice posting, Item Ledger Entry carries the shipment document number
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Posting a sales order with both Ship and Invoice in one call creates the Item Ledger Entry during the shipment leg of that combined post, so the entry's `Document No.` is stamped with the value assigned to the shipment — `Sales Header."Last Shipping No."` — not the posted sales invoice number the posting call returns. Code that filters Item Ledger Entry by the invoice number instead finds nothing: `SetRange`/`FindSet` simply return zero rows, with no error to signal the mistake.
|
||||||
|
|
||||||
|
## Best Practice
|
||||||
|
|
||||||
|
After posting a sales order with Ship and Invoice together, read `SalesHeader."Last Shipping No."` (populated during the post) and filter Item Ledger Entry by that value, not by the invoice number the posting routine returns.
|
||||||
|
|
||||||
|
See sample: `item-ledger-entry-document-no-follows-last-shipping-no.good.al`.
|
||||||
|
|
||||||
|
## Anti Pattern
|
||||||
|
|
||||||
|
Filtering Item Ledger Entry by the posted sales invoice number after a combined Ship-and-Invoice post. The filter compiles and runs without error but matches zero rows, because the entry belongs to the shipment leg of the posting, not the invoice leg.
|
||||||
|
|
||||||
|
See sample: `item-ledger-entry-document-no-follows-last-shipping-no.bad.al`.
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
codeunit 50132 "Sample Customer Type Library"
|
||||||
|
{
|
||||||
|
procedure CreateCustomerType(var CustomerType: Record "Customer Type")
|
||||||
|
begin
|
||||||
|
CustomerType.Init();
|
||||||
|
CustomerType.Code := 'TEST001';
|
||||||
|
CustomerType.Description := 'Test Customer Type';
|
||||||
|
CustomerType.Insert(true);
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
codeunit 50132 "Sample Customer Type Library"
|
||||||
|
{
|
||||||
|
var
|
||||||
|
LibraryUtility: Codeunit "Library - Utility";
|
||||||
|
|
||||||
|
procedure CreateCustomerType(var CustomerType: Record "Customer Type")
|
||||||
|
begin
|
||||||
|
CustomerType.Init();
|
||||||
|
CustomerType.Code := CopyStr(LibraryUtility.GenerateGUID(), 1, MaxStrLen(CustomerType.Code));
|
||||||
|
CustomerType.Description := CopyStr(LibraryUtility.GenerateGUID(), 1, MaxStrLen(CustomerType.Description));
|
||||||
|
CustomerType.Insert(true);
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
bc-version: [all]
|
||||||
|
domain: testing
|
||||||
|
keywords: [generateguid, library-utility, test-fixtures, uniqueness, copystr, maxstrlen]
|
||||||
|
technologies: [al]
|
||||||
|
countries: [w1]
|
||||||
|
application-area: [all]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Generate unique test fixture values with LibraryUtility.GenerateGUID()
|
||||||
|
|
||||||
|
## 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()` returns a value that is unique per call and long enough to guarantee no collision; paired with `CopyStr(..., 1, MaxStrLen(Field))` it fits any fixed-length `Code` or `Text` field safely.
|
||||||
|
|
||||||
|
## Best Practice
|
||||||
|
|
||||||
|
For a fixture field that must be unique across test runs, assign `CopyStr(LibraryUtility.GenerateGUID(), 1, MaxStrLen(TargetField))` rather than a literal string.
|
||||||
|
|
||||||
|
See sample: `use-generateguid-for-unique-test-fixture-values.good.al`.
|
||||||
|
|
||||||
|
## Anti Pattern
|
||||||
|
|
||||||
|
Hardcoding a fixture value such as `'TEST001'` or a short descriptive literal. It collides across parallel or repeated test runs, and a value longer than the field's length limit is either silently truncated or raises an insert error.
|
||||||
|
|
||||||
|
See sample: `use-generateguid-for-unique-test-fixture-values.bad.al`.
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
codeunit 50131 "Sample Customer Type UI Test"
|
||||||
|
{
|
||||||
|
Subtype = Test;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure CustomerTypeFieldIsEnabledOnCustomerCard()
|
||||||
|
var
|
||||||
|
CustomerCard: TestPage "Customer Card";
|
||||||
|
begin
|
||||||
|
// Confirms only that the page opens - never checks the field's actual UI state
|
||||||
|
CustomerCard.OpenView();
|
||||||
|
CustomerCard.Close();
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
codeunit 50131 "Sample Customer Type UI Test"
|
||||||
|
{
|
||||||
|
Subtype = Test;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure CustomerTypeFieldIsEnabledOnCustomerCard()
|
||||||
|
var
|
||||||
|
Assert: Codeunit Assert;
|
||||||
|
CustomerCard: TestPage "Customer Card";
|
||||||
|
begin
|
||||||
|
CustomerCard.OpenView();
|
||||||
|
Assert.IsTrue(CustomerCard."Customer Type".Enabled(), 'Customer Type should be editable on the Customer Card.');
|
||||||
|
Assert.IsTrue(CustomerCard."Customer Type".Visible(), 'Customer Type should be visible on the Customer Card.');
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
bc-version: [all]
|
||||||
|
domain: testing
|
||||||
|
keywords: [testpage, visible, enabled, ui-state, headless-test, field-verification]
|
||||||
|
technologies: [al]
|
||||||
|
countries: [w1]
|
||||||
|
application-area: [all]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Verify field visibility and editability with TestPage.Visible()/.Enabled()
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
A UI test codeunit does not need to inspect table or page properties indirectly to confirm a field is shown or editable under given conditions. The `TestPage` object exposes a `Visible()` and an `Enabled()` function on each field, reflecting the page's actual rendered state, callable directly from a `[Test]` procedure after `OpenView()`.
|
||||||
|
|
||||||
|
## Best Practice
|
||||||
|
|
||||||
|
Open the `TestPage`, navigate to the relevant record if needed, then assert against `TestPageField.Visible()` and `TestPageField.Enabled()` to verify the field's UI state, rather than checking an unrelated table/page property or skipping the check.
|
||||||
|
|
||||||
|
See sample: `use-testpage-visible-enabled-to-verify-field-ui-state.good.al`.
|
||||||
|
|
||||||
|
## Anti Pattern
|
||||||
|
|
||||||
|
A test that opens the `TestPage` but never asserts against `Visible()`/`Enabled()` on the field in question — confirming only that the page opens, not that the field behaves as expected.
|
||||||
|
|
||||||
|
See sample: `use-testpage-visible-enabled-to-verify-field-ui-state.bad.al`.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue