mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 17:36:53 +01:00
Park testing knowledge files for later review
This commit is contained in:
parent
822cae1b27
commit
c4ecab140b
6 changed files with 167 additions and 0 deletions
|
|
@ -0,0 +1,32 @@
|
||||||
|
codeunit 50101 "Sales Discount Tests"
|
||||||
|
{
|
||||||
|
Subtype = Test;
|
||||||
|
TestPermissions = Disabled;
|
||||||
|
var
|
||||||
|
LibrarySales: Codeunit "Library - Sales";
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure PostSalesOrderCreatesHeader()
|
||||||
|
var
|
||||||
|
SalesHeader: Record "Sales Header";
|
||||||
|
begin
|
||||||
|
// No Initialize() - setup bloat repeated in every test.
|
||||||
|
// No LibraryVariableStorage.Clear() - stale handler bindings carry over from prior tests.
|
||||||
|
LibrarySales.SetCreditWarningsToNoWarnings();
|
||||||
|
LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, LibrarySales.CreateCustomerNo());
|
||||||
|
if SalesHeader."No." = '' then Error('Header number not assigned');
|
||||||
|
end;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure SalesInvoiceCarriesSellToCustomer()
|
||||||
|
var
|
||||||
|
SalesHeader: Record "Sales Header";
|
||||||
|
CustomerNo: Code[20];
|
||||||
|
begin
|
||||||
|
// Identical setup pasted again. Change the requirement once, miss it here.
|
||||||
|
LibrarySales.SetCreditWarningsToNoWarnings();
|
||||||
|
CustomerNo := LibrarySales.CreateCustomerNo();
|
||||||
|
LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Invoice, CustomerNo);
|
||||||
|
if SalesHeader."Sell-to Customer No." <> CustomerNo then Error('Sell-to customer mismatch');
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
codeunit 50100 "Sales Discount Tests"
|
||||||
|
{
|
||||||
|
Subtype = Test;
|
||||||
|
TestPermissions = Disabled;
|
||||||
|
var
|
||||||
|
LibrarySales: Codeunit "Library - Sales";
|
||||||
|
LibraryVariableStorage: Codeunit "Library - Variable Storage";
|
||||||
|
IsInitialized: Boolean;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure PostSalesOrderCreatesHeader()
|
||||||
|
var
|
||||||
|
SalesHeader: Record "Sales Header";
|
||||||
|
begin
|
||||||
|
Initialize();
|
||||||
|
LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, LibrarySales.CreateCustomerNo());
|
||||||
|
if SalesHeader."No." = '' then Error('Header number not assigned');
|
||||||
|
end;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure SalesInvoiceCarriesSellToCustomer()
|
||||||
|
var
|
||||||
|
SalesHeader: Record "Sales Header";
|
||||||
|
CustomerNo: Code[20];
|
||||||
|
begin
|
||||||
|
Initialize();
|
||||||
|
CustomerNo := LibrarySales.CreateCustomerNo();
|
||||||
|
LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Invoice, CustomerNo);
|
||||||
|
if SalesHeader."Sell-to Customer No." <> CustomerNo then Error('Sell-to customer mismatch');
|
||||||
|
end;
|
||||||
|
local procedure Initialize()
|
||||||
|
begin
|
||||||
|
LibraryVariableStorage.Clear(); // runs every test, not just on first init
|
||||||
|
if IsInitialized then exit;
|
||||||
|
LibrarySales.SetCreditWarningsToNoWarnings();
|
||||||
|
IsInitialized := true;
|
||||||
|
Commit();
|
||||||
|
end;
|
||||||
|
}
|
||||||
26
community/knowledge/testing/initialize-function-pattern.md
Normal file
26
community/knowledge/testing/initialize-function-pattern.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
bc-version: [all]
|
||||||
|
domain: testing
|
||||||
|
keywords: [initialize, isinitialized, test-isolation, test-pollution, test-setup, libraryvariablestorage]
|
||||||
|
technologies: [al]
|
||||||
|
countries: [w1]
|
||||||
|
application-area: [all]
|
||||||
|
---
|
||||||
|
|
||||||
|
# Test codeunit Initialize function must use an IsInitialized guard
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Every test codeunit should have a single `Initialize()` procedure called as the first statement in each `[Test]` function. Without an `IsInitialized` boolean guard, setup runs before every test — which is slow and causes side effects. Without `Initialize()` at all, setup logic gets duplicated across test functions and shared state bleeds between tests.
|
||||||
|
|
||||||
|
## Best Practice
|
||||||
|
|
||||||
|
Declare `IsInitialized` as a boolean at codeunit scope. In `Initialize()`, call `LibraryVariableStorage.Clear()` first — it must run on every test call regardless of the guard. Then check `if IsInitialized then exit`. Run one-time setup after the guard, set `IsInitialized := true`, then call `Commit()`. Call `Initialize()` as the first statement in every `[Test]` procedure.
|
||||||
|
|
||||||
|
See sample: `initialize-function-pattern.good.al`.
|
||||||
|
|
||||||
|
## Anti Pattern
|
||||||
|
|
||||||
|
Duplicating setup logic directly inside each test function, or calling `Initialize()` without the `IsInitialized` guard. Duplicated setup diverges silently when changed in one place. Omitting `LibraryVariableStorage.Clear()` leaves stale message handlers from prior tests, causing unrelated tests to fail intermittently.
|
||||||
|
|
||||||
|
See sample: `initialize-function-pattern.bad.al`.
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
codeunit 50103 "Customer Permission Tests"
|
||||||
|
{
|
||||||
|
Subtype = Test;
|
||||||
|
// No TestPermissions - defaults to Disabled. All tests run as SUPER.
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure CustomerReadSucceeds()
|
||||||
|
var
|
||||||
|
Customer: Record Customer;
|
||||||
|
begin
|
||||||
|
// SUPER always has read access. This passes even when the real user
|
||||||
|
// calling this feature in production has no Customer read permission.
|
||||||
|
// The test never catches a permission gap. False confidence.
|
||||||
|
Customer.FindFirst();
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
codeunit 50102 "Customer Permission Tests"
|
||||||
|
{
|
||||||
|
Subtype = Test;
|
||||||
|
TestPermissions = Restrictive;
|
||||||
|
var
|
||||||
|
Assert: Codeunit Assert;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
procedure CustomerReadFailsWithoutPermission()
|
||||||
|
var
|
||||||
|
Customer: Record Customer;
|
||||||
|
begin
|
||||||
|
// Restrictive: test runner assigns no permission sets to the test user.
|
||||||
|
asserterror Customer.FindFirst();
|
||||||
|
Assert.ExpectedError('You do not have the following permissions');
|
||||||
|
end;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[TestPermissions(TestPermissions::Disabled)]
|
||||||
|
procedure AdminCanReadAllCustomers()
|
||||||
|
var
|
||||||
|
Customer: Record Customer;
|
||||||
|
begin
|
||||||
|
// Disabled overrides codeunit-level Restrictive for this test only.
|
||||||
|
// Use for paths that legitimately require elevated access.
|
||||||
|
Customer.FindFirst();
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
bc-version: [all]
|
||||||
|
domain: testing
|
||||||
|
keywords: [testpermissions, permissions, restrictive, disabled, super, test-context, permission-set]
|
||||||
|
technologies: [al]
|
||||||
|
countries: [w1]
|
||||||
|
application-area: [all]
|
||||||
|
---
|
||||||
|
|
||||||
|
# TestPermissions attribute controls the permission set under which a test runs
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The `TestPermissions` property on a test codeunit — or the `[TestPermissions]` attribute on an individual test function — controls which permission set the test executes under. When omitted, the test runs with the permissions of the calling user, typically SUPER in a development environment. Tests that pass under SUPER may fail for real users with standard permission sets.
|
||||||
|
|
||||||
|
## Best Practice
|
||||||
|
|
||||||
|
Set `TestPermissions = Restrictive` at the codeunit level. This runs every test with no permission sets assigned, catching missing `InherentPermissions` or `PermissionSet` grants before they reach production. Override at the function level with `[TestPermissions(TestPermissions::Disabled)]` only for tests that explicitly verify admin-level behavior.
|
||||||
|
|
||||||
|
See sample: `testpermissions-attribute-controls-test-context.good.al`.
|
||||||
|
|
||||||
|
## Anti Pattern
|
||||||
|
|
||||||
|
Omitting `TestPermissions` entirely, or setting `Disabled` at the codeunit level. Both cause every test to run as SUPER. Permission errors that real users would hit are invisible, and the test suite gives false confidence about the app's behavior under realistic conditions.
|
||||||
|
|
||||||
|
See sample: `testpermissions-attribute-controls-test-context.bad.al`.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue