131005 “Library – Dialog Handler” – build test handlers differently

by Dec 3, 2022AL Language

Home 9 Development 9 AL Language 9 131005 “Library – Dialog Handler” – build test handlers differently

Test UI Handlers are necessary in many cases if you want to test any logic that shows confirm, message, or different page… The most common are ConfirmHandler (to handle Confirm()) and MessageHandler (to handle Message()). To find out more about Handlers, check Microsoft Learn.

This article will take a different approach to creating handlers than what’s published at Microsoft Learn – using W1 Library “Library – Dialog Handler”.

Let’s start with a basic example of a test with a standard confirm handler (a real test from one of my projects).

[Test]
[HandlerFunctions('NotReservedReleaseForWhseConfirmHandler')]
procedure TestSomething()
var
    SalesHeader: Record "Sales Header";
    SalesLine: Record "Sales Line";
begin
    // [GIVEN] Sales Retail Order exists with not reserved line
    PTELibrarySales.CreateSalesRetailOrder(SalesHeader);
    LibrarySales.CreateSalesLineWithUnitPrice(SalesLine, SalesHeader, '', 5, 2);
    SalesLine.SetRecFilter();

    // [WHEN] The order is manually released to warehouse 
    PTELibrarySales.ReleaseRetailSalesOrderToWhse(SalesHeader);

    // [THEN] The confirm is shown, released for warehouse status is set to Released and not reserved line is removed
    // NotReservedReleaseForWhseConfirmHandler handler
    LibraryAssert.AreEqual(SalesHeader."TKA Released for Ret. Whse."::Released, SalesHeader."TKA Released for Ret. Whse.", SalesHeader.FieldCaption("TKA Released for Ret. Whse."));
    LibraryAssert.RecordIsEmpty(SalesLine);
end;

[ConfirmHandler]
procedure NotReservedReleaseForWhseConfirmHandler(Question: Text[1024]; var Reply: Boolean);
var
    NotReservedReleaseForWhseQst: Label 'Some of lines are not fully reserved. Do you want to release the order for warehouse?';
begin
    LibraryAssert.ExpectedMessage(NotReservedReleaseForWhseQst, Question);
    Reply := true;
end;

There is nothing wrong with this approach. However, there are some limitations/problems that I want to mitigate. Firstly, if you want to check the order of how the handlers are called, the code required (with global integer/boolean/… variables) is strange… Also, if your test should cause more than one Confirm/Message/StrMenu, it is a bit problematic (again, you need some global variables) to check whether all dialogs were shown. Lastly, I prefer to have the full test info within one procedure.

So what’s my solution? Codeunit 131005 “Library – Dialog Handler”

I found a test library codeunit 131005 “Library – Dialog Handler”, which allows you to build better handlers (at least, better for me). It is not used anywhere in W1 tests (?), but it’s used in some localisation tests (Czech tests).

codeunit 131005 "Library - Dialog Handler"
{
    var
        Assert: Codeunit Assert;
        LibraryVariableStorage: Codeunit "Library - Variable Storage";

    procedure HandleMessage(Message: Text)
    begin
        Assert.ExpectedMessage(LibraryVariableStorage.DequeueText(), Message);
    end;

    procedure HandleConfirm(Question: Text; var Reply: Boolean)
    begin
        Assert.ExpectedConfirm(LibraryVariableStorage.DequeueText(), Question);
        Reply := LibraryVariableStorage.DequeueBoolean();
    end;

    procedure HandleStrMenu(Options: Text; var Choice: Integer; Instruction: Text)
    begin
        Assert.ExpectedStrMenu(
            LibraryVariableStorage.DequeueText(), LibraryVariableStorage.DequeueText(),
            Instruction, Options);
        Choice := LibraryVariableStorage.DequeueInteger();
    end;

    procedure SetExpectedMessage(Message: Text)
    begin
        LibraryVariableStorage.Enqueue(Message);
    end;

    procedure SetExpectedConfirm(Question: Text; Reply: Boolean)
    begin
        LibraryVariableStorage.Enqueue(Question);
        LibraryVariableStorage.Enqueue(Reply);
    end;

    procedure SetExpectedStrMenu(Options: Text; Choice: Integer; Instruction: Text)
    begin
        LibraryVariableStorage.Enqueue(Instruction);
        LibraryVariableStorage.Enqueue(Options);
        LibraryVariableStorage.Enqueue(Choice);
    end;

    procedure ClearVariableStorage()
    begin
        LibraryVariableStorage.Clear();
    end;
}

As you can see from the code above, it is not only for Confirms but also for Messages and StrMenus. The codeunit is pretty simple – it only stores all expected handlers using “Library – Variable Storage” to queue (= FIFO processing) and retrieve it when it should be handled.

See refactored example below; changes are

  • Initialize() procedure called at the beginning of each test to clear existing handler definitions using ClearVariableStorage()
  • Simpler Handler – it just calls HandleConfirm() procedure from LibraryDialogHandler
    • It will always be the same (so you could add it to your custom snippets if you are using it)
  • LibraryDialogHandler.SetExpectedConfirm(NotReservedReleaseForWhseQst, true);
    • The most important thing – the definitions of confirm/message/strmenu
    • Add it just before the line that should cause the dialog = you can check the order of dialogs, and if the test should cause more than one dialog of the same type, it’s much easier to do it this way than using the standard handler procedure.
local procedure Initialize();
begin
    LibraryDialogHandler.ClearVariableStorage();
end;

[Test]
[HandlerFunctions('ConfirmHandler')]
procedure TestSomething()
var
    SalesHeader: Record "Sales Header";
    SalesLine: Record "Sales Line";
    NotReservedReleaseForWhseQst: Label 'Some of lines are not fully reserved. Do you want to release the order for warehouse?';
begin
    Initialize();
    // [GIVEN] Sales Retail Order exists with not reserved line
    PTELibrarySales.CreateSalesRetailOrder(SalesHeader);
    LibrarySales.CreateSalesLineWithUnitPrice(SalesLine, SalesHeader, '', 5, 2);
    SalesLine.SetRecFilter();

    // [WHEN] The order is manually released to warehouse with confirmation
    LibraryDialogHandler.SetExpectedConfirm(NotReservedReleaseForWhseQst, true);
    PTELibrarySales.ReleaseRetailSalesOrderToWhse(SalesHeader);

    // [THEN] Released for warehouse status is set to Released and not reserved line is removed
    LibraryAssert.AreEqual(SalesHeader."TKA Released for Ret. Whse."::Released, SalesHeader."TKA Released for Ret. Whse.", SalesHeader.FieldCaption("TKA Released for Ret. Whse."));
    LibraryAssert.RecordIsEmpty(SalesLine);
end;

[ConfirmHandler]
procedure ConfirmHandler(Question: Text[1024]; var Reply: Boolean);
begin
    LibraryDialogHandler.HandleConfirm(Question, Reply);
end;

MessageHandlers and StrMenuHandlers are the same – only one line to call HandleXXX procedure from the “Library – Dialog Handler” codeunit.

[MessageHandler]
procedure MessageHandler(Message: Text[1024]);
begin
    LibraryDialogHandler.HandleMessage(Message);
end;

[StrMenuHandler]
procedure StrMenuHandler(Options: Text[1024]; var Choice: Integer; Instruction: Text[1024]);
begin
    LibraryDialogHandler.HandleStrMenu(Options, Choice, Instruction);
end;

Recent Articles from the category

BC Open Source? How to start?

BC Open Source? How to start?

BC Open Source? How to start? One of the most exciting news introduced last month in Lyon during Directions EMEA 2023 was the changes to the open-source initiative. This means that you can now contribute to the source code of the Base app and the System app, which are...

read more
Validate a FlowField Field. Wait? What?

Validate a FlowField Field. Wait? What?

Validate a FlowField Field. Wait? What? There are not many things in the AL Language that surprised me. However, last week, I found one such thing - I reviewed customizations made by another partner and had to analyze the OOTB code of the Demand Forecast matrix. I run...

read more
Dynamics NAV 2013 & Expired Cronus License

Dynamics NAV 2013 & Expired Cronus License

We found an interesting problem - we were not able to run the development environment for Dynamics NAV 2013. Whenever we tried to run the development client, we got the following error message: "Your program license has expired" and the development client has closed...

read more
Indirect Dependencies and Access Modifiers

Indirect Dependencies and Access Modifiers

Last week, there was a discussion on Yammer on how to get values from the "Sent Email" record when all fields are marked as Internal. I was surprised that many people do not know what can/can't access modifiers (such as local, protected, or internal) be used for. I...

read more

Sign Up for News

Certifications

Highest certification
Microsoft Data Management and
also in D365 Business Central

Microsoft Certified: Dynamics 365 Business Central Functional Consultant Associate

See other certifications here