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 to create repeatable tests, and helpful to create tests that can be automated. This article describes the features in Business Central that help you test the business logic in your application, and it provides some best practices for testing.
For a walkthrough concerning advanced extension testing, see Testing the Advanced Extension Sample.
Business Central includes the below features 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||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 that one or two hours a day.|
|On-premises||Production||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||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.
You can reuse test runners from Test Runner module in the Microsoft/ALAppExtensions GitHub repo. You can also use the repo to request for the new functionality.
Test pages mimic actual 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 any kind of page. 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 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 Testing Pages.
To create tests that can be automated, you must handle cases when user interaction is requested by 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 write code in the ConfirmHandler method to verify that the expected question is displayed by the Confirm Method. You write AL code to return the relevant reply.
You create a specific handler for each page that you want to handle and a specific report handler for each report that you want to handle.
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 do not run the test codeunit from a test runner codeunit, then any unhandled UI is displayed as it typically would.
For more information, see Creating Handler Methods.
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
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.
Use ASSERTERROR in test code only. It isn't allowed or supported in production code.
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 test 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.
In positive tests, the test method should validate the results of application calls, such as return values, state changes, or database transactions.
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:
Initialize and set up the conditions for the test.
Invoke the business logic that you want to test.
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. Mandatoryfield in the
Purchases & Payables Setuptable. 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.
Use the Any module in the Microsoft/ALAppExtensions GitHub repo 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.