In this instance, our test case will create data that will used to test that the records are inserted correctly and the methods perform the correct behavior.
The test will be to create a number of vehicle service records and test the validateField
, modifiedField
, and insert
methods. We also want to test creating a number of these records to test performance of this table.
The steps involved in this scenario are the same as before, except that we need to tear down the data we created. We will also see that a test case can actually contain far more code than the code that we are testing. This is normal, and we expect to see a 3:1 ratio of test code to business logic.
The framework supports automatic teardown of test data using company isolation. This is done by creating a temporary company and deleting it after the test method or class completes.
We control the isolation of our test case by overriding the createSuite
method and returning a new instance of one of the following methods:
SysTestSuite
: This does not provide automatic teardown, and must be manually cleaned up in the class's tear-down methodSysTestSuiteCompanyIsolateClass
: A company will be created, the test methods executed, and then the company is deletedSysTestSuiteCompanyIsolateMethod
: A company will be created per test method, isolating each test method's data from each otherWe will use the SysTestSuiteCompanyIsolateClass
suite for our test, as we want to create a reasonable amount of setup data and test each method on this data.
There is a second part to this, and that is whether the test case should run in a single instance. The default is false
, which means that the class is instantiated for each test method. This means that the setup method will be executed for each test method with no teardown or cognizance of the other instances of the class. So, if we are using company isolation by class, we normally do want the class to execute as one instance and prevent the data setup execution for subsequent test methods.
The steps used to create the unit test case are as follows:
ConFMSVehServiceTableTest
.[SysTestTargetAttribute(tableStr(ConFMSVehServiceTable), UtilElementType::Table)] class ConFMSVehServiceTableTest extends SysTestCase { int numberOfServiceRecordsToCreate; int numberOfVehicles; // set up is run for each test method // we don't want the data created for each // test method as we will isolate at class level boolean dataIsReady; }
setUp
method. It is written as follows:private void createTestSetupData() { ConFMSVehicleTable vehicle; ConFMSVehicleGroup vehGroup; ConFMSVehicleSchedule schedule; Counter idx; ConFMSVehicleId vehicleId; vehGroup.clear(); vehGroup.VehicleGroupId = "TestGroup"; vehGroup.insert(); schedule.clear(); schedule.VehicleScheduleId = "1"; schedule.Days = 30; schedule.Distance = 2000; schedule.insert(); schedule.clear(); schedule.VehicleScheduleId = "2"; schedule.Days = 60; schedule.Distance = 4000; schedule.insert(); schedule.clear(); schedule.VehicleScheduleId = "3"; schedule.Days = 365; schedule.Distance = 30000; schedule.insert(); for(idx = 1; idx <= numberOfVehicles; idx++) { vehicleId = strFmt("V%1", strReplace( strRFix(int2str(idx),5), " ", "0")); vehicle.clear(); vehicle.VehicleId = vehicleId; vehicle.Name = strFmt("Vehicle %1", vehicleId); vehicle.VehicleGroupId = vehGroup.VehicleGroupId; vehicle.VehicleSchedId = int2str((idx mod 3) + 1); vehicle.insert(); } dataIsReady = true; }
setUp
method—right-click on the class and go to Override method | setUp.public void setUp() { // These parameter variables will be also // be used by the test methods numberOfServiceRecordsToCreate = 20; numberOfVehicles = 200; if(!isDataReady) this.createTestSetupData(); super(); }
createSuite
so that it returns a new instance of SysTestSuiteCompanyIsolateClass
:public SysTestSuite createSuite() { return new SysTestSuiteCompanyIsolateClass(this); }
isDataReady
variable will always be false
. Override the useSingleInstance
method and return true
, as shown here:public boolean useSingleInstance() { return true; }
[SysTestMethodAttribute] public void testInitFromConFMSVehicleTable() { ConFMSVehicleTable vehicle; ConFMSVehServiceTable vehServices; while select vehicle { vehServices.clear(); vehServices.initFromConFMSVehicleTable(vehicle); this.assertEquals(vehicle.VehicleId, vehServices.VehicleId, strFmt("Vehicle %1 initialised", vehicle.VehicleId)); } } [SysTestMethodAttribute] public void testValidateField() { ConFMSVehicleTable vehicle; ConFMSVehServiceTable vehServices; Counter idx; ConFMSVehicleSchedId serviceSchedId; Days serviceDaysExpected; Distance serviceDistanceExpected; while select vehicle { for(idx = 1; idx<= numberOfServiceRecordsToCreate; idx ++) { vehServices.clear(); vehServices.VehicleId = vehicle.VehicleId; // test invalid data vehServices.ServiceDate = systemDateGet() + 5; vehServices.NextServiceDate = vehServices.ServiceDate - 2; this.assertFalse(vehServices.validateField( fieldNum(ConFMSVehServiceTable, NextServiceDate))); vehServices.CurrentOdometer = 1000; vehServices.NextServiceOdo = vehServices.CurrentOdometer-2; this.assertFalse(vehServices.validateField( fieldNum(ConFMSVehServiceTable, NextServiceOdo))); // test valid data vehServices.ServiceDate = systemDateGet() + 5; vehServices.modifiedField( fieldNum(ConFMSVehServiceTable, ServiceDate)); this.assertTrue(vehServices.validateField( fieldNum(ConFMSVehServiceTable, NextServiceDate))); vehServices.CurrentOdometer = 1000; vehServices.modifiedField( fieldNum(ConFMSVehServiceTable, CurrentOdometer)); this.assertTrue(vehServices.validateField( fieldNum(ConFMSVehServiceTable, NextServiceOdo))); } } } [SysTestMethodAttribute] public void testModifiedField() { ConFMSVehicleTable vehicle; ConFMSVehServiceTable vehServices; Counter idx; ConFMSVehicleSchedId schedId; Days serviceDaysExpected; Distance serviceDistanceExpected; TransDate serviceDateExpected; while select vehicle { for(idx = 1; idx<= numberOfServiceRecordsToCreate; idx++) { vehServices.clear(); vehServices.VehicleId = vehicle.VehicleId; vehServices.ServiceDate = systemDateGet(); vehServices.modifiedField( fieldNum(ConFMSVehServiceTable, ServiceDate)); schedId = vehicle.VehicleSchedId; serviceDateExpected = dateNull(); if(ConFMSVehicleSchedule::exist(schedId)) { serviceDaysExpected = ConFMSVehicleSchedule:: find(schedId).Days; serviceDateExpected = vehServices.ServiceDate + serviceDaysExpected; } this.assertEquals(serviceDateExpected, vehServices.NextServiceDate); vehServices.CurrentOdometer = 1000; vehServices.modifiedField( fieldNum(ConFMSVehServiceTable, CurrentOdometer)); serviceDistanceExpected = 0; serviceSchedId = vehicle.VehicleSchedId; if(ConFMSVehicleSchedule::exist(schedId)) { serviceDistanceExpected = vehServices.CurrentOdometer + ConFMSVehicleSchedule:: find(schedId).Distance; } this.assertEquals(serviceDistanceExpected, vehServices.NextServiceOdo); } } } [SysTestMethodAttribute] public void testInsert() { ConFMSVehicleTable vehicle; ConFMSVehServiceTable vehServices; Counter idx; while select vehicle { for(idx = 1; idx<= numberOfServiceRecordsToCreate; idx++) { vehServices.clear(); vehServices.initFromConFMSVehicleTable( vehicle); vehServices.ServiceId = vehicle.VehicleId+int2str(idx); vehServices.insert(); this.assertEquals(vehicle.VehicleId, vehServices.VehicleId, strFmt("Vehicle %1 inserted", vehicle.VehicleId)); this.assertNotEqual(vehServices.RecId, 0, strFmt("Vehicle %1 inserted", vehicle.VehicleId)); } } }
As before, the system will provide the results of the test in the Infolog window, marking the test methods as succeeded or failed. In the following example, a test method had a compilation error:
In our example, we used company isolation of data. This creates a company for each class, runs the setup method to create the setup data, and deletes the company after the test class completes. This is sufficient in our custom code, as the test data is very simple. However, this is insufficient for many testing requirements, as the configuration data is just as important as the code. This is usually the case when we are running the tests to validate code on the test environment. In this case, we can run our test cases without isolation, use SQL database backup and restore as the setup and teardown steps. This is easily possible only when using the TFS development method, where each developer has their own instance, which is another advantage of using this method of development.
18.216.27.251