Bootcamp Notes: Testing

In NAV2009 we already had the option to create test CodeUnits, but NAV2013 made our options much more extensive.
Basically, how do we avoid breaking stuff when developing? By testing, or even better, by automated testing. One of the biggest advantages is testing failures… When something needs to fail, an error stops our code, with the testability framework this is not true.

Before I start, let’s begin mentioning that also Microsoft uses their own test CodeUnits, which are actually freely available for download here.

In NAV2009, we needed to set the client to  be able to run test CodeUnits by changing a property in the options. NAV2013 no longer requires this, when a CodeUnit is of type test, it runs…

A CodeUnit has a property named SubType which can be Normal (default), Test or TestRunner.
Running a codeunit of type test will execute all global functions of type Test regardless of errors in it. OnRun will still fail with an error. If an error occurs in a test function, the function will return failed, otherwise, it will return success. TestRunner is a CodeUnit which can execute multiple Test CodeUnits

For test CodeUnits it may be necessary to expect an error, this is why the statement ASSERTERROR exists. Place the statement in front of the code call/line you expect an error to happen, and the function will not fail. After the line has executed, you can use the GETLASTERRORTEXT to check if the error was the error you expected, so the function actually succeeds when it fails. Even more, if you put the assert error statement, and no error happens, the function will return failure.

When you are testing, you really want to automate everything. If you just call function, you might notice confirms, strmenu, messages, … are still shown and keep your tests waiting for input. This is actually not something you really want in automated tests. For this we have other types of functions, called handlers (Possible types: MessageHandler, ConfirmHandler, StrMenuHandler, PageHandler, ModalPageHandler, ReportHandler and RequestPageHandler). These types of function require specific parameters, which you can see when trying to compile. A ConfirmHandler for instance needs 2 parameters: Question Text 1024 and VAR Answer boolean.

After creating a handler, you need to attach it to the function you want to use the handler by putting the function name in the HandlerFunctions property of the function. In this property, you can supply multiple handler functions separated by a comma. Supplying 2 handlers of the same type, will only execute the first one.

In much the same way, when you attach a handler to a function, the function expects the handler to actually fire or the function fails. A good way to do this, is to create 1 handler of each type per CodeUnit and set the expected question, … Maybe a little example here:

The first test function will succeed, the second will fail. To even improve, you can create a BeginTest and an EndTest normal function in the codeunit. The BeginTest would reset all context for all handlers, the EndTest would fire all handlers so the function does not fail if the handler is not fired, because it will already fire if there is a confirm you did not expect. Then call BeginTest before every function and EndTest at the end of every function. This way, you can attach all handlers to all functions to make sure everything will continue without waiting for input.

TestRunners are intended to automate test execution. You can run multiple test CodeUnits at once and allow code the check if they need to run, and even intercept the success or failure and write to a table. For this you need to create 2 functions: OnBeforeTestRun and OnAfterTestRun. These do need a specific signature, which you can see when trying to compile, but also below this line:

FunctionName can be blank to enable you to skip the entire codeunit.

Another issue when testing is re-usability of tests. Imagine you depend on some invoice, but once it is posted, you need to create it again. you could write ASSERTERROR ERROR(”); to roll back, except…
If you have a commit in your code, it will only rollback to the commit.
For this, we have a property on every test function called Transaction Type:
– Autocommit (default): all changes are executed after running
– Autorollback: will completely rollback
– None: use for page testability, will generate an error that a transaction must be started if you use it without a page…

So auto rollback looks good right? No… It will actually fail if there is a commit in your code, so basically, you can use this property to test if there is no commit in your code. Which is also its main purpose fail if it runs into a commit…

Don’t we have any option whatsoever to fetch this and do a rollback? Well, since 2013 we do have this, but it is located in a TestRunner CodeUnit. These have a property called TestIsolation:
– Disabled (default): normal procedure, it looks to properties of the test functions
– Codeunit: After every test CodeUnit there will be a rollback, EVEN when there are commits
– Function: Same for codeunit, except the rollback is per function.

NAV is using the beauty of nested transactions on SQL Server to make sure they can do a rollback even with a commit in there.

However… Don’t you just love that there always is a however or a but? Well, make sure you only call test CodeUnits in your test runner, because any error that happens in the TestRunner itself could mess up your TestIsolation type.

Test Pages

Something completely new in NAV2013 is test pages. Because with test CodeUnits we can test all functions and business logic, but not what is happening on a page. Basically, we make a test CodeUnit with a test function. In this test function, we create a variable of type TestPage with a reference to the actual page you wish to test. When viewing the properties, you will notice you can invoke functions such as OpenNew, OpenEdit, GetField and call all actions available on the page. All fields are shown, even those set to visible or fields which are variables on the pages that are put visible.

You can create another variable of type TestPage and call the method TRAP of it. Then the next instance of that page will be “trapped” into your TestPage variable instead of showing to the user. This can be more convenient than creating a handler function.

By the way, do not forget to set your Transaction Type of the function to none!

This is still a “quick” and small overview of testability in NAV, just play around with it and look at all the options you have. Also, check out the regression test of Microsoft themselves, freely available for download here.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.