Respond to @nikolakukrika's review on #62: - Merge ui-calls-require-test-handlers, handlerfunctions-attribute-must-match-ui-path and handlers-enqueue-never-assert into a single ui-handlers-in-tests article. - Adopt the enqueue-from-test / dequeue-and-assert-in-handler pattern using Assert.ExpectedConfirm/ExpectedMessage (substring match), with Initialize() clearing LibraryVariableStorage and AssertEmpty() proving exact call counts. - asserterror sample now uses Assert.ExpectedTestFieldError + FieldCaption instead of hardcoded message/code; article text points to the library helpers. - Drop the tablerelation article and fold its test-relevant ordering point (relations checked on Validate/Insert(true); build parents first) into use-library-codeunits-for-test-fixtures. Article count 198 -> 195. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2.9 KiB
| bc-version | domain | keywords | technologies | countries | application-area | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
testing |
|
|
|
|
Wire and verify UI handlers with enqueue-driven expectations
Description
A test runs headless: there is no interactive user to answer a dialog. Every UI call the executed path raises — Confirm, Message, error dialogs, Page.Run/RunModal, Report.Run/RunModal, request pages, StrMenu, Notification.Send — must be intercepted by a handler carrying the matching attribute ([ConfirmHandler], [MessageHandler], [StrMenuHandler], [ModalPageHandler], …) and named in the method's [HandlerFunctions(...)]. The list is a two-sided contract: raise a UI call with no listed handler and the platform aborts with an unhandled UI error; list a handler the path never hits and it fails with "handler function was not executed". Both are runtime failures — the test never reaches its verdict, so a reviewer sees an infrastructure error instead of a result on the behavior under test.
Getting the handler present is only half the job; the handler must also verify the right dialog fired the right number of times. Do that by driving handlers from the test, not by hardcoding answers inside them.
Best Practice
Make the test own the expectations and the handlers consume them. Before acting, the test Enqueues — in interaction order — the expected text (a stable substring) and any reply each handler must return. The handler Dequeues the expected text, verifies it with the purpose-built asserts (Assert.ExpectedMessage, Assert.ExpectedConfirm, Assert.ExpectedStrMenu — which match on a fragment, not the full localized caption), then Dequeues and returns its reply. Finish the test body with LibraryVariableStorage.AssertEmpty to prove every enqueued interaction fired exactly once, and start each test with an Initialize that calls LibraryVariableStorage.Clear so a value leaked by an earlier test cannot cascade. List in [HandlerFunctions] precisely the handlers the scenario triggers — no superset "just in case", no subset that happens to work today.
See sample: ui-handlers-in-tests.good.al.
Anti Pattern
Omitting a handler for a UI call the path raises (unhandled-UI abort), padding the list with a handler the path never reaches ("handler function was not executed"), or writing handlers that hardcode their answer and assert inline with no enqueue/dequeue. The last is the subtle one: nothing proves the correct dialog fired the expected number of times, and an inline assertion that fails inside a handler can be swallowed by the calling UI operation, leaving the suite green while the behavior is broken. Skipping Initialize/AssertEmpty hides both a leaked queue and a missing or extra dialog.
See sample: ui-handlers-in-tests.bad.al.