Testing the application overview

Before you release your Business Central application, you should test its functionality to ensure it works as expected. Testing is an iterative process. It's important and helpful to create repeatable tests that can be automated. This article describes the features in Business Central to help you test the business logic in your application and provides best practices for testing.

For a walkthrough concerning advanced extension testing, see Testing the Advanced Extension Sample.

Business Central includes the features that are listed below to help you test your application.

Environment testing support and limitations

The extent to which you can run automated tests will depend on your Business Central solution type and environment. The following table gives an overview.

Business Central solution Environment Testing allowed More details
Online Production Running tests isn't allowed because it might have an adverse effect on your business. Testing can incidentally invoke external systems, like CDS, PayPal, and web hook subscriptions. Invoking these systems may slow down the solution for other users or cause data corruption.
Sandbox check mark for feature. You can use a sandbox environment to run tests manually to verify functionality on an environment. Running a large number of tests or tests that take a long time (more than 15 minutes per test method) isn't allowed. It's recommended that you don't run tests more than one or two hours a day.
On-premises Production check mark for feature. For Business Central on-premises, running automated tests is only possible with a Partner license or a license that includes the Application Builder module.

You can disable the ability to run tests by turning off Enable Test Automation (TestAutomationEnabled) on the Business Central Server instance. For more information, see Configuring Business Central Server - General Settings.
Container-based development environment check mark for feature. This setup should be the default environment for running large number of tests or setting up CI/CD gates. For more information, see Running a Container-Based Development Environment or Running Tests In Containers.

Test Codeunits and Test Methods

You write tests as AL code in methods of codeunits that are configured to be test codeunits. Test codeunits have the SubType Property set to Test. There are three types of methods that you can add in a test codeunit: test, handler, and normal. Each method type is used for a specific purpose and behaves differently. When a test codeunit runs, it executes the OnRun trigger, and then executes each test method in the codeunit. The outcome of a test method is either SUCCESS or FAILURE.

This pattern doesn't apply to test isolation and isn't recommended as a method for running tests.

For more information about test codeunits and test methods, see Test Codeunits and Test Methods.

Test runner codeunits

You use test runner codeunits to manage the execution of test codeunits and to integrate with other test management, execution, and reporting frameworks. By integrating with a test management framework, you can automate your tests and enable them to run unattended.

Test runner codeunits are codeunits that have the SubType Property set to TestRunner.

Test runner codeunits include the following triggers:

In the OnRun trigger, you enter the code to run the codeunits. It runs when you execute the codeunit and before the test methods run. You can use the OnBeforeTestRun and the OnAfterTestRun triggers to do preprocessing and postprocessing, such as initialization or logging test results.

For more information about test runner codeunits, see Test Runner Codeunits.

Tip

You can reuse test runners from Test Runner in the BCApps repository. You can also use the repo to request for the new functionality or even contribute it yourself.

Test pages

Test pages mimic actual application pages but don't present any UI on a client computer. Test pages let you test the code on a page by using AL to simulate user interaction with the page.

There are two types of test pages:

  • TestPage, which is a regular page and can be of any kind. It includes page parts and subpages as well.

  • TestRequestPage, which represents the request page on a report.

You access the page's fields and properties or a field by using the dot notation. You can open and close test pages, do actions on the test page, and navigate around the test page by using AL methods. For more information, see Test pages.

UI handlers

To create tests that can be automated, you must handle cases when user interaction is requested by the code that is being tested. UI handlers run instead of the requested UI. UI handlers provide the same exit state as the UI. For example, a method that has the ConfirmHandler Attribute set, handles Confirm Method calls. If code that is being tested calls the Confirm Method, then the ConfirmHandler method is called instead of the Confirm Method. You can write code in the ConfirmHandler method to verify that the expected question is displayed by the Confirm Method. You can also write AL code to return the relevant reply.

For each page and report that you want to handle, you need to create a specific handler for the page and a specific report handler for the report.

If you run a test codeunit from a test runner codeunit, then any unhandled UI in the test methods of the test codeunit causes a failure of the test. If you don't run the test codeunit from a test runner codeunit, then any unhandled UI is displayed as it typically would.

For more information, see Create Handler Methods.

ASSERTERROR Keyword

You use AssertError statements in test methods to test how your application behaves under failing conditions. These statements are called positive and negative tests. The AssertError keyword specifies that an error is expected at run time in the statement that follows the AssertError keyword.

If a simple or compound statement that follows the AssertError keyword causes an error, then execution successfully continues to the next statement in the test method. You can get the error text of the statement by using the GETLASTERRORTEXT Method.

If a statement that follows the AssertError keyword doesn't cause an error, then the AssertError statement causes the following error and the test method that is running produces a FAILURE result.

Important

Use ASSERTERROR in a test code only. It isn't allowed or supported in the production code.

Example

To create a test method to test the result of a failure of a CheckDate method that you've defined, you can use the following code. This example requires that you create a method called CheckDate. This method checks whether the date is valid for the customized application. You also create the following text constant, Date variable InvalidDate, and Text variable InvalidDateErrorMessage.

InvalidDate := 010184D;
InvalidDateErrorMessage := 'The date is outside the valid date range.';
ASSERTERROR CheckDate(InvalidDate);
if GETLASTERRORTEXT <> InvalidDateErrorMessage then
  ERROR('Unexpected error: %1', GETLASTERRORTEXT);

Testing best practices

We recommend the following best practices for designing your application tests:

  • Test code should be kept separate from the code that is being tested. That way, you can release the tested code to a production environment without releasing the test code.

  • Test code should determine that the code works as intended both under successful and failing conditions. These tests are called positive and negative tests. The positive tests validate that the code being tested works as intended under successful conditions. The negative tests validate that the code being tested work as intended under failing conditions.

    1. In positive tests, the test method should validate the results of application calls, such as return values, state changes, or database transactions.

    2. In negative tests, the test method should validate that the intended errors occur, error messages are presented, and data has the expected values.

  • Automated tests shouldn't require user intervention.

  • Tests should leave the system in the same well-known state as when the test started. This way, you can rerun the test or run other tests in any order and always start from the same state.

  • Test execution and reporting should be fast and able to integrate with the test management system. This way, the tests can be used as check-in tests or other build verification tests. These other tests typically run on unattended servers.

  • Create test methods that follow the same pattern:

    1. Initialize and set up the conditions for the test.

    2. Invoke the business logic that you want to test.

    3. Validate that the business logic worked as expected.

  • Only use hardcoded values in tests when you really need it. For all other data, consider using random data.

    For example, you want to test the Ext. Doc. No. Mandatory field in the Purchases & Payables Setup table. To do this, you need to create and post typical purchase invoice. The typical purchase invoice line specifies an amount. For most tests, it doesn't matter exactly what amount. For inspiration, see the use of the GenerateRandomCode method in the tests that are included in the TestToolkit folder on the Business Central product media. For more information, see Random Test Data.

    Tip

    Use the Any library in the BCApps repository to generate pseudo-random values during test set-up. This module generates the same set of numbers, allowing you to reproduce test failures.

  • Tests should be readable and fast to execute. We recommend that test codeunits run under 2 minutes, and that you don't add more than 100 test methods to the codeunit.

Test pages

Create Handler Methods
Test Codeunits and Test Methods
Application Testing Example: Testing Purchase Invoice Discounts
Random Test Data
Testing the Advanced Extension Sample