Unit testing helps ensure that the code fulfills the requirements at hand and that future changes (even in other packages) do not cause a regression. The unit test is written as a separate package that references the package it is testing. If we follow Test-Driven Development (TDD), we would write the tests early in the process, and some would argue first. TDD changes the way we think when writing code. Should we need to make a change to a project, we are forced to update the test-case code (as the tests will otherwise fail). This promotes a test-centric approach to development and naturally reduces the test cycles. Regression in other packages is caught by the build process. The build server will download all checked-in code, perform a build, and then look for tests to execute. Any tests that fail are reported and the build, depending on the build's setup, will...
Technical requirements
You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Extending-Microsoft-Dynamics-365-Finance-and-Supply-Chain-Management-Cookbook-Second-Edition/blob/master/Chapter%2012%20-%20Unit%20Tests.axpp.
Creating a unit test project
To write a test case, we need to reference the Testing Essentials package. This package is not deployed to sandbox or production environments, which means that any deployment that depends on the test framework will fail. Therefore, we must have a new package for our unit tests. The best way to write tests is to write them as we would write our main code. Therefore, we would add a test project to the same solution as the development project.
Let's say we created a project for a credit check change called ConSalesCreditCheck in the Contoso package. The structure would be similar to the following:
- Solution: ConSalesCreditCheck
- Project: ConSalesCreditCheck (Package Contoso)
- Project: ConSalesCreditCheckTest (Package ContosoTest)
Creating a unit test case for code
In this recipe, we will create three test cases, starting with two simple tests to test the vehicle and vehicle table code and then a test case for the ConVehicleGroupChange class. These tests should test when it should fail and when it should succeed. This process will involve programmatically creating some test data in order to perform the tests.
Getting ready
We will just need the unit test project, which we created in the previous recipe, open. Also, on the main menu, select X64 from Test | Test Setting | Default Processor Architecture.
How to do it...
Creating an Acceptance test library entity class
In the Application Explorer, there are hundreds of classes prefixed with atl. These classes are part of the Acceptance test library and are used to simplify and greatly reduce the code that's required to write test cases. Not only can these classes create setup data that is required to run a test, but also execute processes such as running a command to change the vehicle group. It may seem that we are blurring the lines between unit and integration tests. Unit tests aren't intended to test configuration data, and attempting this too early will complicate this process. At this stage, we will use atl to create some default setup data in order to write and execute unit tests. Before this framework was made available, we needed combinations of task recordings and databases that contain the base data for the tests.
The next...
Creating an Acceptance test library specification class
The specification class defines acceptance criteria and replaces the manual this.Assert...() methods. This means we can write our acceptance criteria before the tests are actually written. These are usually written in conjunction with a query class, so in this recipe, we will create both and update the test to use this new pattern.
Getting ready
This recipe follows the previous recipe. As a minimum, we need a package that references the following packages:
- Testing essentials
- Acceptance test library – Foundation
- Acceptance test library – Application suite (if using standard entities in your tests)
- An entity class for the table we are going to test, such...
Creating an Acceptance test library data class
The data class brings the query and specification classes together, and in its simplest form, it simply constructs and returns instances of the query and specification classes. We will also pull together the previous Acceptance test library recipes to create a more typical test scenario.
In this recipe, we will complete the vehicle group test suite with an AtlData class, but also create a test to test the creation of a vehicle with the correct default vehicle group.
Getting ready
This recipe follows the previous recipe. As a minimum, we need a package that references the following packages:
- Testing essentials.
- Acceptance test library – Foundation.
- Acceptance test library...
Creating an Acceptance test library data class structure
Now, we will create a full set of Atl classes to support the testing and creation of a vehicle. This involves integrating them into the AtlDataRoot class in order to access the suite fluently.
The class structure will be as follows:
- AtlDataConVMS: The root node for the vehicle management system:
- VehicleGroups as the AtlDataConVMSVehicleGroups type
- Parameters as the AtlDataConVMSParameters type
- Cars as the AtlDataConVMSVehicleTableCars type
- VehicleGroups as the AtlDataConVMSVehicleGroups type
Getting ready
This recipe follows the previous recipe. As a minimum, we need a package that references the following packages:
- Testing essentials.
- Acceptance test library – Foundation.
- Acceptance test library –...
Creating an Acceptance test library class that ensures a process can be done
One of the benefits of writing ATL classes is that the classes create their own data, which means we can tell them to ensure that a process can be performed.
For example, we can't create a vehicle without a vehicle group, and we know from the code that we also need a default vehicle group. Here, we need to write a method starting with ensure, such as ensureCanCreateVehicle().
To demonstrate this, we will add ConVMSVehicleTableCars to our ATL data class structure.
Getting ready
This recipe follows the previous recipe. As a minimum, we need a package that references the following packages:
- Testing essentials.
- Acceptance test library –...