Back to T280: Testing Business Logic
Lesson 4: Testing the Display of Errors and Warnings
The topics of this chapter describe how to test whether error or warning messages are displayed properly on the UI
when users perform particular actions.
Testing of Errors and Warnings: General Information
The logic of a graph can include the displaying of errors and warnings on the UI. In a test method, you can verify that certain actions cause an error or warning to be displayed.
Learning Objectives
In this chapter, you will learn how to do the following:
- Test the logic of a graph that has a setup DAC
- Test whether a proper error is attached to a field
- Test whether a proper warning is attached to a field
- Clear the cache of errors
- Find the proper DAC object in the cache
Applicable Scenarios
In unit tests, you can check whether an appropriate warning or error is attached to a box when the entered value does not fit the logic of the form.
Creation of an Instance of a Graph That Has a Setup DAC
If a tested graph has setup DACs, you must call the Setup
Testing of the Displayed Errors and Warnings
The tested graph may contain the logic that attaches an error or warning to a DAC field in some circumstances. The use of the PXCache.GetStateExt<> generic method is the recommended way to get the state of a field to test whether an error or warning is attached to the field. You can also use the PXCache.GetError, PXCache.GetErrors, and PXCache.GetWarning method for it. You pass the class that corresponds to the tested field as the type parameter of the PXCache.GetStateExt<> generic method, and you pass the tested DAC object as the parameter of this method. The method returns a PXFieldState object for which the following are true:
- The object's Error property contains the error message.
- The Warning property of the object contains the warning message.
- The object's ErrorLevel property contains the level of the warning or error attached. (The value of this
property is PXErrorLevel.Error if there is an error attached, and PXErrorLevel.Warning if there
is a warning attached.)
Lesson 4: Testing the Display of Errors and Warnings | 31
In your code, aer you have tested the displaying of an error or warning, you clear the cache of errors and warnings with the PXCache.Clear method. The attaching of an error or warning to a field may be the result of an action that generates an exception or cancels the assignment of a value. In this case, the cache may contain different DAC objects before and aer performing such an action. To pick the proper DAC object for testing, you call the PXCache.Locate method and pass an object of that DAC type having the correct values of the key fields.
Activity 4.1: To Test the Display of Errors and Warnings
The following activity will walk you through the process of creating a method that tests the display of warning and error messages.
Story
Suppose that you want to make sure that the following behavior of the Repair Work Orders (RS301000) form has not changed:
- When a new work order is initialized, the system copies to it the repair items and labor items of the RSSVRepairPrice object that has the same DeviceID and ServiceID values as the work order being initialized.
- Negative values of labor quantities are prohibited.
- The values of labor quantities are not permitted to be less than the minimum values.
- In work orders for which the priority is Low, repair services that are marked as requiring preliminary checks are prohibited. You need to create a test method. In this method, you need to initialize the settings of the graph.
Process Overview
You will create a test method without parameters for the Repair Work Orders (RS301000) form. In this method, you will initialize the settings of the tested graph RSSVWorkOrderEntry by calling the Setup<> generic method. Then you will create an instance of the RSSVWorkOrderEntry graph. In the cache of the graph, you will create the necessary objects and configure them. You will assign to the fields values that are not allowed by the business logic of the graph and ensure that the appropriate warnings or errors appear in the UI; you will do this by calling the PXCache.GetStateExt method.
System Preparation
Before you begin creating the test method, create the RSSVWorkOrderEntryTests test class. For an example that shows how to create a test class, see Activity 1.2: To Create a Test Class.
Step 1: Creating a Test Method for the Repair Work Orders Form In this step, you will create a test method for the Repair Work Orders (RS301000) form. (For basic information on the creation of a test method without parameters, see Test Method: General Information.) Do the following:
- In the RSSVWorkOrderEntryTests class, create a public void method, and name it TestRepairWorkOrdersForm.
- Specify the Fact attribute for the method to indicate that this unit test is called without parameters.
- Initialize the settings for the RSSVWorkOrderEntry graph by adding the following code.
Setup<RSSVWorkOrderEntry>(new RSSVSetup());
Lesson 4: Testing the Display of Errors and Warnings | 32
The RSSVSetup DAC contains settings for the RSSVWorkOrderEntry graph. The call of the Setup<>
generic method is mandatory because it initializes an RSSVSetup object.
4. Create an instance of the RSSVWorkOrderEntry graph as follows.
var graph = PXGraph.CreateInstance<RSSVWorkOrderEntry>();
Step 2: Creating the Necessary Objects In the TestRepairWorkOrdersForm method, create the objects that are needed to test the displaying of warnings and errors as follows:
- Create an RSSVDevice object and two RSSVRepairService objects (one of these is the main object,
and the other is auxiliary) by adding the following code.
RSSVDevice device = (RSSVDevice)graph.Caches[typeof(RSSVDevice)]. Insert(new RSSVDevice { DeviceCD = "Device1" }); RSSVRepairService repairService = (RSSVRepairService)graph.Caches[typeof(RSSVRepairService)]. Insert(new RSSVRepairService { ServiceCD = "Service1" }); RSSVRepairService repairService2 = (RSSVRepairService)graph.Caches[typeof(RSSVRepairService)]. Insert(new RSSVRepairService { ServiceCD = "Service2" }); - Create an RSSVRepairPrice object, and initialize it with the RSSVDevice object and the main
RSSVRepairService object. You do this by adding the following code.
graph.Caches[typeof(RSSVRepairPrice)].Insert(new RSSVRepairPrice { DeviceID = device.DeviceID, ServiceID = repairService.ServiceID }); - Make sure that you have added the using PX.Objects.IN; directive to the RSSVWorkOrderEntryTests class.
- Add the following code to create two stock items and one non-stock item, and to make the two stock items
repair items.
InventoryItem battery1 = (InventoryItem)graph.Caches[typeof(InventoryItem)].Insert(new InventoryItem { InventoryCD = "Battery1" }); InventoryItemExt batteryExt1 = battery1.GetExtension<InventoryItemExt>(); batteryExt1.UsrRepairItem = true; batteryExt1.UsrRepairItemType = RepairItemTypeConstants.Battery; graph.Caches[typeof(InventoryItem)].Update(battery1);
Lesson 4: Testing the Display of Errors and Warnings | 33
InventoryItem backCover1 =
(InventoryItem)graph.Caches[typeof(InventoryItem)].Insert(new
InventoryItem
{
InventoryCD = "BackCover1"
});
InventoryItemExt backCoverExt1 =
backCover1.GetExtension<InventoryItemExt>();
backCoverExt1.UsrRepairItem = true;
backCoverExt1.UsrRepairItemType =
RepairItemTypeConstants.BackCover;
graph.Caches[typeof(InventoryItem)].Update(backCover1);
InventoryItem work1 =
(InventoryItem)graph.Caches[typeof(InventoryItem)].Insert(new
InventoryItem
{
InventoryCD = "Work1",
StkItem = false
});
5. Create two repair items based on the two stock items as follows.
RSSVRepairItem repairItemBackCover1 =
(RSSVRepairItem)graph.Caches[typeof(RSSVRepairItem)].Insert(
new RSSVRepairItem
{
DeviceID = device.DeviceID,
ServiceID = repairService.ServiceID
});
repairItemBackCover1.InventoryID = backCover1.InventoryID;
repairItemBackCover1.Required = true;
repairItemBackCover1.BasePrice = 10;
repairItemBackCover1.IsDefault = true;
repairItemBackCover1.RepairItemType =
backCoverExt1.UsrRepairItemType;
graph.Caches[typeof(RSSVRepairItem)].Update(repairItemBackCover1);
RSSVRepairItem repairItemBattery1 =
(RSSVRepairItem)graph.Caches[typeof(RSSVRepairItem)].Insert(
new RSSVRepairItem
{
DeviceID = device.DeviceID,
ServiceID = repairService.ServiceID
});
repairItemBattery1.InventoryID = battery1.InventoryID;
repairItemBattery1.Required = true;
repairItemBattery1.BasePrice = 20;
repairItemBattery1.IsDefault = true;
repairItemBattery1.RepairItemType = batteryExt1.UsrRepairItemType;
graph.Caches[typeof(RSSVRepairItem)].Update(repairItemBattery1);
6. Create an RSSVLabor object based on the non-stock item by using the following code.
RSSVLabor labor = (RSSVLabor)graph.Caches[typeof(RSSVLabor)].
Insert(new RSSVLabor
{
InventoryID = work1.InventoryID,
Lesson 4: Testing the Display of Errors and Warnings | 34
DeviceID = device.DeviceID,
ServiceID = repairService.ServiceID
});
labor.DefaultPrice = 2;
labor.Quantity = 3;
graph.Caches[typeof(RSSVLabor)].Update(labor);
7. Create a work order with the same DeviceID and ServiceID values as those specified for the RSSVRepairPrice object, which was created in Instruction 2. To do this, add the following code.
RSSVWorkOrder workOrder = (RSSVWorkOrder)graph.
Caches[typeof(RSSVWorkOrder)].Insert(new RSSVWorkOrder());
workOrder.DeviceID = device.DeviceID;
workOrder.ServiceID = repairService.ServiceID;
graph.Caches[typeof(RSSVWorkOrder)].Update(workOrder);
Step 3: Testing the Display of Errors and Warnings Now that the necessary objects have been created, perform the following instructions to test the display of errors and warnings:
- As the first test, add the following code to make sure that there are two RSSVWorkOrderItem objects,
which have been created based on the two RSSVRepairItem objects, and an RSSVWorkOrderLabor
object, which has been created based on the RSSVLabor object.
Assert.Equal(2, graph.RepairItems.Select().Count); Assert.Single(graph.Labor.Select()); - As the second test, add the following code to ensure that the changing of the ServiceID field of the work
order does not affect the RepairItems and Labor views.
workOrder.ServiceID = repairService2.ServiceID; graph.Caches[typeof(RSSVWorkOrder)].Update(workOrder); Assert.Equal(2, graph.RepairItems.Select().Count); Assert.Single(graph.Labor.Select()); - Restore the ServiceID value as follows.
workOrder.ServiceID = repairService.ServiceID; graph.Caches[typeof(RSSVWorkOrder)].Update(workOrder); - Obtain the RSSVWorkOrderLabor object (the value of its Quantity field must be 3), and try to assign a
negative value to its Quantity field by using the following code. An error message must be attached to the
Quantity field instead.
RSSVWorkOrderLabor woLabor = graph.Labor.SelectSingle(); Assert.Equal(3, woLabor.Quantity); woLabor.Quantity = -1; graph.Caches[typeof(RSSVWorkOrderLabor)].Update(woLabor); PXFieldState fieldState = (PXFieldState)graph.Caches[typeof(RSSVWorkOrderLabor)]. GetStateExt<RSSVWorkOrderLabor.quantity>(woLabor); Assert.Equal(PhoneRepairShop.Messages.QuantityCannotBeNegative, fieldState.Error); Assert.Equal(PXErrorLevel.Error, fieldState.ErrorLevel); graph.Labor.Cache.Clear();
Lesson 4: Testing the Display of Errors and Warnings | 35
In the code, the PXCache.Clear method is called to clear the cache of the generated error. 5. By using the following code, assign to the Quantity field of the RSSVWorkOrderLabor object a value that is less than the value of the corresponding RSSVLabor.Quantity field. Then make sure that the following result is achieved: A warning message is attached to the RSSVWorkOrderLabor.Quantity field, and the field is assigned the value of the corresponding RSSVLabor.Quantity field.
woLabor.Quantity = 1;
graph.Caches[typeof(RSSVWorkOrderLabor)].Update(woLabor);
woLabor = (RSSVWorkOrderLabor)graph.
Caches[typeof(RSSVWorkOrderLabor)].Locate(woLabor);
fieldState = (PXFieldState)graph.
Caches[typeof(RSSVWorkOrderLabor)].
GetStateExt<RSSVWorkOrderLabor.quantity>(woLabor);
Assert.Equal(PhoneRepairShop.Messages.QuantityTooSmall,
fieldState.Error);
Assert.Equal(PXErrorLevel.Warning, fieldState.ErrorLevel);
Assert.Equal(3, woLabor.Quantity);
In the code, you use the PXCache.Locate method to obtain the proper RSSVWorkOrderLabor object from the cache to check the presence and text of the warning. 6. By using the following code, configure the second repair service repairService2 to require a preliminary check, and assign it to the work order. The code also sets the priority of the work order to Low and makes sure that an error message is attached to the Priority field of the work order; this error message informs the user that the priority of the work order is too low.
repairService2.PreliminaryCheck = true;
graph.Caches[typeof(RSSVRepairService)].Update(repairService2);
workOrder.ServiceID = repairService2.ServiceID;
workOrder.Priority = WorkOrderPriorityConstants.Low;
graph.Caches[typeof(RSSVWorkOrder)].Update(workOrder);
workOrder = (RSSVWorkOrder)graph.Caches[typeof(RSSVWorkOrder)].
Locate(workOrder);
fieldState = (PXFieldState)graph.Caches[typeof(RSSVWorkOrder)].
GetStateExt<RSSVWorkOrder.priority>(workOrder);
Assert.Equal(PhoneRepairShop.Messages.PriorityTooLow, fieldState.Error);
Assert.Equal(PXErrorLevel.Error, fieldState.ErrorLevel);
7. Run the test method you have created, and make sure that it succeeds. Appendix: Initial Configuration | 36
Appendix: Initial Configuration If for some reason you cannot complete the instructions in To Deploy an Instance for the Training Course, you can create an Acumatica ERP instance and manually publish the needed customization project as described in this topic.
Step 1: Deploying the Needed Acumatica ERP Instance for the Training Course You deploy an Acumatica ERP instance and configure it as follows:
- To deploy a new application instance, open the Acumatica ERP Configuration wizard, and do the following: a. On the Database Configuration page, type the name of the database: SmartFix_T280. b. On the Tenant Setup page, set up a tenant with the T100 data inserted by specifying the following settings:
- Tenant Name: MyTenant
- New: Selected
- Insert Data: T100
- Parent Tenant ID: 1
- Visible: Selected c. On the Instance Configuration page, in the Local Path of the Instance box, select a folder that is outside of the C:\Program Files (x86), C:\Program Files, and C:\Users folder. We recommend that you store the website folder outside of these folders to avoid an issue with permission to work in these folders when you perform customization of the website. The system creates a new Acumatica ERP instance, adds a new tenant, and loads the selected data to it.
- Sign in to the new tenant by using the following credentials:
- Username: admin
- Password: setup Change the password when the system prompts you to do so.
Step 2: Publishing the Required Customization Project You load the customization project with the results of the T220 Data Entry and Setup Forms training course and publish this project as follows:
- On the Customization Projects (SM204505) form, create a project with the name PhoneRepairShop, and open it.
- In the menu of the Customization Project Editor, click Source Control > Open Project from Folder.
- In the dialog box that opens, specify the path to the Customization\T220\PhoneRepairShop folder, which you have downloaded from Acumatica GitHub, and click OK.
- Bind the customization project to the source code of the extension library as follows:
a. Copy the Customization\T220\PhoneRepairShop_Code folder to the App_Data\Projects
folder of the website.
Appendix: Initial Configuration | 37
By default, the system uses the App_Data\Projects folder of the website as the parent folder for the solution projects of extension libraries. If the website folder is outside of the C:\Program Files (x86), C:\Program Files, and C:\Users folders, we recommend that you use the App_Data\Projects folder for the project of the extension library. If the website folder is in the C:\Program Files (x86), C:\Program Files, or C: \Users folder, we recommend that you store the project outside of these folders to avoid an issue with permission to work in these folders. In this case, you need to update the links to the website and library references in the project. b. Open the solution, and build the PhoneRepairShop_Code project. c. Reload the Customization Project Editor. d. In the menu of the Customization Project Editor, click Extension Library > Bind to Existing. e. In the dialog box that opens, specify the path to the App_Data\Projects \PhoneRepairShop_Code folder, and click OK. - In the menu of the Customization Project Editor, click Publish > Publish Current Project.
The Modified Files Detected dialog box opens before publication because you have rebuilt the extension library in the PhoneRepairShop_Code Visual Studio project. The Bin \PhoneRepairShop_Code.dll file has been modified, and you need to update it in the project before the publication.
The published customization project contains all changes to the Acumatica ERP website and database that have been performed in the T200 Maintenance Forms, T210 Customized Forms and Master-Detail Relationship, and T220 Data Entry and Setup Forms training courses. This project also contains the customization plug-ins that fill in the tables created in the T200 Maintenance Forms, T210 Customized Forms and Master-Detail Relationship, and T220 Data Entry and Setup Forms training courses with the custom data entered in these training courses. For details about the customization plug-ins, see To Add a Customization Plug-In to a Project. The creation of customization plug-ins is outside of the scope of this course.