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

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
AL Extensions: Replace Document Attachment

AL Extensions: Replace Document Attachment

I have published a new simple, open-source extension that allows replacing existing document attachments in all master entities as well as in open documents. The source code as well as the app file that can be installed in your environment is available on my GitHub...

read more
Clean up your copied environments

Clean up your copied environments

One of the most important things every developer should handle is to clean up the environment when the environment (or a company) is copied. Especially if the environment is managed directly by a client and they can create new copies anytime. Similarly, for copied...

read more
7 git commands you should know

7 git commands you should know

For many years, developers in C/AL did not need to know anything about Git or other versioning tools. That has changed with Business Central, or more specifically with AL Language. Today, we will look at the most important (and basic) git commands any developer should...

read more
How to define the source for item reservations?

How to define the source for item reservations?

It's not uncommon to have a customer's request to limit from which source items could be reserved. For example, customers may not want to reserve items from return orders. How can we achieve this goal? It's really simple. All the magic is done in the procedure...

read more
NavigationAction for ErrorInfo data type

NavigationAction for ErrorInfo data type

One more article about ErrorInfo data type. Have you already read my previous posts about ErrorInfo and Collectible Errors? ErrorInfo data type & Collectible Errors | MSDyn365 Business Central - Ing. Tomáš Kapitán (kepty.cz) Collectible Errors?! | MSDyn365...

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