bcquality/microsoft/knowledge/testing/ui-handlers-in-tests.md
wenjiefan c213f1495e Exempt optional notification handlers from the HandlerFunctions execution rule
The narrowed UI-handler guidance still stated the execution rule without the
qualifier the linked Microsoft reference uses. The article said every listed
handler must execute at least once, and the testing leaf skill asked for
`[HandlerFunctions(...)]` to match the invoked handlers exactly. The reference
says every *nonoptional* listed handler must execute, and that send-notification
and recall-notification handlers can be optional. As written, an agent could
flag a deliberately unused optional notification handler.

The discriminator is narrower than the handler type. Both
`[SendNotificationHandler([HandlerIsOptional: Boolean])]` and
`[RecallNotificationHandler([HandlerIsOptional: Boolean])]` take an explicit
optionality argument, so `[SendNotificationHandler(true)]` is exempt while the
same attribute written without the argument stays nonoptional like every other
handler type. Keying the exemption on the argument rather than the type keeps it
checkable from the diff and avoids the opposite false positive, where an agent
stops flagging genuinely nonoptional notification handlers.

Changes:

- The article now states the nonoptional qualifier, explains that optionality is
  declared rather than inferred, and adds an explicit do-not-flag clause. That
  clause also forbids proposing removal, because the listed entry is what keeps
  the test passing on the runs where the notification does fire.
- The testing leaf skill carries the same boundary in its `ui-handlers-in-tests`
  cue, and its mechanical-fix list no longer allows removing a listed optional
  notification handler as a one-click suggestion.
- `SendNotificationHandler` and `RecallNotificationHandler` were missing from the
  skill's testing token list, so notification handlers were not reliably
  surfaced to the relevance step at all. Both are now listed.
- The good sample gains a test that lists an unreached
  `[SendNotificationHandler(true)]`; the bad sample gains the mirror image, an
  unreached `[SendNotificationHandler]` with no optionality argument. The pair
  differs only by that argument, which is the point.
- `evaluation/review-fixtures.json` pins the testing domain to
  `ui-handlers-in-tests` so the boundary is exercised: the good sample is the
  clean control at `minimumCleanRate` 1.0 and the bad sample is the expected
  finding. Keywords were retagged with `notification` and `optional-handler`.

validate_frontmatter.py reports 0 errors; Test-ReviewFixtures.ps1 passes with
32 cases across 16 leaf domains and resolves the testing fixture to this article.
2026-09-02 11:46:13 +02:00

3 KiB

bc-version domain keywords technologies countries application-area
all
testing
handler
handlerfunctions
confirm
message
notification
optional-handler
enqueue
capture
runmodal
unhandled-ui
al
w1
all

Wire UI handlers and verify meaningful outcomes

Description

A test runs headless, so every UI call on the executed path must be intercepted by a matching handler named in [HandlerFunctions(...)]. The list is a two-sided contract: an unhandled UI call aborts the test, while Microsoft documents that every nonoptional listed handler must execute at least once or the test fails.

Optionality is declared, not inferred. SendNotificationHandler and RecallNotificationHandler accept a HandlerIsOptional argument, so [SendNotificationHandler(true)] may stay listed on a run that never raises the notification, while the same attribute written without that argument is nonoptional like every other handler type. Notifications are conditional by nature, so an optional notification handler is listed precisely because the scenario may or may not reach it.

Beyond that wiring guarantee, the test must verify the behavior it cares about. The appropriate pattern depends on the contract: a handler can capture concrete page state or a result and the test can assert that semantic postcondition after RunModal; assertions inside a handler are also supported. Queue/enqueue/dequeue and LibraryVariableStorage.AssertEmpty are useful when interaction order, count, text, replies, or a scripted sequence is itself part of the contract, but they are not mandatory for every handler.

Best Practice

List the handlers the scenario triggers, keep an optional notification handler listed for a notification the scenario may conditionally raise, and make each executed handler contribute meaningful evidence. For a single modal page, reset a capture variable before the action, capture a concrete value from the page in the handler, and assert the expected value after RunModal. For ordered or repeated interactions, let the test enqueue expectations, let handlers dequeue and verify them, clear storage during initialization, and finish with AssertEmpty.

See sample: ui-handlers-in-tests.good.al.

Anti Pattern

Omitting a handler for a UI call, listing a nonoptional handler the path never reaches, or claiming action success from a Boolean set before the action runs. A handler that only closes a page can also leave the test without a semantic assertion. Do not flag the absence of queue storage by itself; require it only when the test needs to prove interaction order, count, text, replies, or a scripted sequence. Do not flag a listed [SendNotificationHandler(true)] or [RecallNotificationHandler(true)] that the run does not reach, and never propose removing one: the entry is what keeps the test passing on the runs where the notification does fire.

See sample: ui-handlers-in-tests.bad.al.