Functional Testing in NAV 2009 SP1

Because SP1 for Microsoft Dynamics NAV 2009 was released, I tried to create short demo of the new functionality, which allows developers to create tests for their code. Official documentation for this part of NAV 2009 SP1 could be found here.


How it works

You have new property on codeunit –Subtype – which could be “Normal,Test,TestRunner”.

Normal – you know it… ;-)

  • Test – this codeunit have functions, which are testing the functionality, this is the main codeunit doing the testing
  • TestRunner – this is codeunit which runs the test codeunits and e.g. collecting the results. Because you can run all tests by this one codeunit, it is not problem to run the tests e.g. in NAS or through webservices.


If you use Test subtype, you can use new property on each function in the codeunit – FunctionType – which could be

  • Normal – again you know it…
  • Test – this is the function which tests the functionality
  • MessageHandler – handles cases, when you expect that some message will be displayed
  • ConfirmHandler – handler for Confirm dialog, in which you can simulate the answer by user (yes,no)
  • StrMnuHandler – same like ConfirmHandler but for String menu dialogs
  • FormHandler – you can handle displayed form which you expect
  • ModalFormHandler – same for modal form, you can simulate the result (Yes, No, Ok, Cancel, LookupOK, LookupCancel etc.)
  • ReportHandler – handling reports which are runned by the tested function

Using the handlers could be complex task, how to create them is described here.

Each test function could have assigned one or more handlers, and if the handler is not used during the testing, it leads to test Failed result (it means you are expecting something but it didn’t happened).

In testing function you can use new keyword ASSERTERROR before some command to say that you are expecting error in the command or block of commands. After that you can test if correct error was raised by testing GETLASTERRORTEXT. If there is wrong error text, you can call ERROR and the test function will have result Fail, else it will be Success.

TestRunner codeunit could have two functions – OnBeforeTestRun and OnAfterTestRun – which could e.g. store the date and time of start of some test function, result of the test etc. More info is here.

Short Demo

1st Step – something to test

Because creating the tests is not easy task, best is to test only the base functions you are using. I cannot imagine to create test for testing all aspects of posting codeunit or something like that.

We will start by creating some function which we will test. I have created one codeunit with one function with one parameter of type boolean and result of type boolean. Logic is this:

If parameter is True, Confirm is displayed and if user select Yes, function return true. If User select No, function display Error with text ‘Canceled’.

If parameter is False, function return False.

The function looks like this:

PROCEDURE TestedFunction@1000000001(Parameter@1000000000 : Boolean) : Boolean;
  IF Parameter THEN BEGIN
    IF CONFIRM('My Confirm',TRUE) THEN

You can see that I have used hardcoded texts. Please, take it as something, which will not happen in real development, I have used it to have as simple example as possible.


2nd Step – Testing codeunit

Ok, now we will create the test for this function. We want to test all three paths – Parameter = true, Answer = Yes; Parameter = true; Answer = No; Parameter = False. It means we will use three test functions:

  • TestMyFunctionTrueYes
  • TestMyFunctionTrueNo
  • TestMyFunctionFalse

All three functions are of type ”Test”, whole codeunit is of type “Test”. There are no parameters or return datatype for these functions.

There is the code for these functions:

PROCEDURE TestMyFunctionTrueYes@1000000000();
  TestedFunction@1000000000 : Codeunit 50010;
  IF NOT TestedFunction.TestedFunction(TRUE) THEN
    ERROR('Wrong Answer');

PROCEDURE TestMyFunctionTrueNo@1000000001();
  TestedFunction@1000000000 : Codeunit 50010;
  ASSERTERROR TestedFunction.TestedFunction(TRUE);
    ERROR('Unexpected error: %1', GETLASTERRORTEXT);

PROCEDURE TestMyFunctionFalse@1000000006();
  TestedFunction@1000000003 : Codeunit 50010;
  ErrorText@1000000002 : TextConst 'ENU=The update has been interrupted to respect the warning.';
  IF TestedFunction.TestedFunction(FALSE) THEN
    ERROR('Wrong answer');


Again, I have used hardcoded texts, do not use them in your real development!

You can see, that first two functions have assigned Handler Function. One is simulating the answer Yes, one answer No. In second function we are expecting error, thus ASSERTERROR was used. Than we test if the correct error was triggered.

Now how the handlers looks like:

PROCEDURE ConfirmHandlerYes@1000000002(Question@1000000000 : Text[1024];VAR Answer@1000000001 : Boolean);
  IF Question <> 'My Confirm'  THEN
    ERROR('Unknown Confirm text: %1',Question);
  Answer := TRUE;

PROCEDURE ConfirmHandlerNo@1000000003(Question@1000000001 : Text[1024];VAR Answer@1000000000 : Boolean);
  IF Question <> 'My Confirm'  THEN
    ERROR('Unknown Confirm text: %1',Question);
  Answer := FALSE;


Both are ConfirmHandlers, we are handling the confirm dialog. We are testing that correct confirm dialog was displayed.

Now we have the testing codeunit. Ok, what’s next?

3rd Step – Test runner codeunit

Now we just prepare codeunit which will run this test. This codeunit could be more complex than in our example, e.g. it could run tests based on some setup table, where you can select which tests to run etc. I will give you example of both functions which could be used in this codeunit.

Start with creating new codeunit, select the Test runner subtype. In the OnRun trigger add this line:

CODEUNIT.RUN(CODEUNIT::"Test codeunit");

That’s all if you want only to test the functionality. If you want to do something more, e.g. log the results, you can add these two functions:



  Before@1000000000 : DateTime;
  TestResult@1000000001 : Record 50000;

PROCEDURE OnBeforeTestRun@1000000002(CodeunitID@1000000000 : Integer;CodeunitName@1000000001 : Text[30];FunctionName@1000000002 : Text[30]) : Boolean;

PROCEDURE OnAfterTestRun@1000000003(CodeunitID@1000000002 : Integer;CodeunitName@1000000001 : Text[30];FunctionName@1000000000 : Text[30];Success@1000000003 : Boolean);
  TestResult."Entry No." := 0;
  TestResult."Codeunit ID" := CodeunitID;
  TestResult."Codeunit Name" := CodeunitName;
  TestResult."Function Name" := FunctionName;
  TestResult."Test Start" := Before;
  TestResult."Test End" := CURRENTDATETIME;
  IF  Success THEN
    TestResult.Status := TestResult.Status::Success
    TestResult.Status := TestResult.Status::Failure;
    TestResult."Error Text" := GETLASTERRORTEXT;

TestResult global variable is using new table I have created. You can create it easily, just look at the code, it is clear which datatype is used and how to name the fields. The PK of the table is AutoIncrement=Yes.

4th Step – Run the test

Ok, what to say – Run the Test Runner… :-D

Now you can play with the tested function and e.g. modify the logic to return wrong error or wrong result. All will lead to Failed test.



Ok, now you have imagination how it works. But, are we able to use it to test some complex functionality? That’s the question. I do not know. I am a beginner in Test Driven Development, I can imagine using it for some basic functions, may be that when we will test each function, it will mean that complex functionality is tested too, but I cannot imagine that Customer will want to pay for the time we will spend by creating the test functions. Or, may be he will pay, if the result will be much less bugs which costs money too… What’s your opinion on this? Is it possible to create e.g.. Fully tested Addon? Biggest problem will be the GUI and functionality connected to the GUI, e.g. when I wanted to test the Availability warning, if it is correctly displayed, I was not able to do it, because the warning isconditioned by CurrFieldNo and I am not able to simulate it (the warning is not displayed when the functionality is called from code).

  • You should also know, that Tools -> Options -> Show C/AL Testability properties should be set to YES in order to use testing functionality.

  • Just look into the MSDN documentation. The handlers are there to "handle" different dialog windows which could be displayed during testing. In this way, you can check that some error was displayed, or handle different confirms, messages, forms and reports during the testing. If you add some handler into the HandlerFunctions of the test function, it means that all these handlers must be fired to have positive result. If some handler is not displayed, you will have Fail result of the test. Check properties of all functions if they are set to correct values. Sometime it seems that when copying some functions, the properties are not copied or are reset.

  • Thanks for this blog.

    I've started with this topic trying you code. May you or somebody has me a hint for my tzwo questions:

    1) I get the message "Missing ConfirmHandler UI handler (for Object ID: 0)." in my log.

    Why? Or What went wrong?

    2)I don't understand the property "HandlerFunctions" and the both functions "ConfirmHandler..."

  • CTest is a "test framework" written in NAV.  It is OK, but I think if it is re-done to use this new functionality you talk about here, it would be better.  Still, it only tests from the inside.  To test from the outside (using forms or pages and simulating real user inputs), we will probably need to wait for the Navision Test Framework (NTF).

  • And how do you want to use it on NAV? I think that CTest is good for testing C++ or other "application" which could be compiled into application, but cannot be used for NAV... or it could be used?