Test Data Factory - це підхід до створення тестових даних в Apex тестах. Він дозволяє створювати вхідні дані для тестів у зручному форматі, що дозволяє зменшити кількість коду, необхідного для створення цих даних і полегшити підтримку тестів в майбутньому.
Тестові дані можна створювати в Test Data Factory, використовуючи класи Apex, які відповідають моделям даних. Ці класи можуть містити методи, які створюють записи в базі даних з відповідними значеннями поля. Підхід Test Data Factory має кілька переваг для тестування Apex коду.
Ось декілька з них:
-
Зручне створення вхідних даних: Test Data Factory дозволяє створювати вхідні дані для тестів у зручному форматі, що дозволяє ефективно зменшити кількість коду, необхідного для створення цих даних.
-
Легкість підтримки тестів: Test Data Factory дозволяє створювати тестові дані в одному місці, що зменшує кількість коду, що потрібно підтримувати.
-
Зменшення ризику помилки: Test Data Factory дозволяє зменшити кількість помилок, пов’язаних зі створенням вхідних даних для тестів, тому що даний підхід дозволяє створювати дані в структурованому форматі.
-
Збільшення швидкодії виконання тестів: Test Data Factory дозволяє створювати вхідні дані для тестів в оптимальному форматі, що дозволяє зменшити час виконання тестів і покращити їхню швидкодію.
Отже, використання Test Data Factory є хорошою практикою для створення тестових даних в Apex тестах. Він дозволяє зменшити кількість коду, збільшити швидкість тестування.
Давайте розглянемо приклад. Отже, у нас є два тригери для Account та Order. Їх код наведений ниже.
Тригер для Account:
trigger AccountTrigger on Account (before update) {
// Loop through the accounts being updated in this trigger context.
for(Account accountItem : Trigger.new) {
// Check if the Industry field of the account is null.
if (accountItem.Industry == null) {
// If Industry is null, set its value to 'Other'.
accountItem.Industry = 'Other';
}
}
}
Тригер для Order:
trigger OrderTrigger on Order (before update) {
// Get the current user's ID.
Id userId = UserInfo.getUserId();
User userDetails = [
SELECT Id,
Name,
Email,
Profile.Name,
UserRole.Name
FROM User
WHERE Id = :userId
];
// Extract the UserRole Name from the retrieved user details.
String roleName = userDetails.UserRole.Name;
// Loop through the orders being updated in this trigger context.
for (Order orderItem : Trigger.new) {
// Check if the order has a discount, total order price is greater than 2000, and the user is from the 'Marketing Team'.
if (orderItem.hasDiscount__c == true
&& orderItem.Total_order_price__c > 2000
&& roleName == 'Marketing Team') {
// Apply a discount of 150 to the total order price.
orderItem.Total_order_price__c = orderItem.Total_order_price__c - 150;
}
}
}
Як виглядають тести для цих тригерів, якщо ми не використовуємо DataFactory:
Тест для Order тригеру:
@isTest
public class OrderTriggerTest {
// Test setup method to create necessary records and user for testing.
@testSetup
static void testSetup() {
// Get the IDs of System Administrator profile and Marketing Team role.
String AdminProfileId = [
SELECT Id
FROM Profile
WHERE Name = 'System Administrator'
LIMIT 1
].Id;
String MarketingTeamroleId = [
SELECT Id
FROM UserRole
WHERE Name = 'Marketing Team'
LIMIT 1
].Id;
// Create a user with Marketing Team role.
User userMarketingTeam = new User(
Alias = 'adm',
Country = 'United Kingdom',
Email = 'adm.man@test.com',
EmailEncodingKey = 'UTF-8',
LastName = 'admin',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = AdminProfileId,
TimeZoneSidKey = 'America/Los_Angeles',
UserName = 'admin@test.tradingscreen.com',
UserRoleId = MarketingTeamroleId
);
insert userMarketingTeam;
// Run tests within the context of the Marketing Team user.
System.runAs(userMarketingTeam) {
// Create test records: Account, Contact, Contract, and Order.
Account newAccount = new Account();
newAccount.Name = 'TestAccount';
insert newAccount;
Contact newContact = new Contact();
newContact.FirstName = 'Joe';
newContact.LastName = 'Test';
insert newContact;
Contract newContract = new Contract();
newContract.AccountId = newAccount.Id;
newContract.Status = 'Draft';
newContract.StartDate = Date.today();
newContract.ContractTerm = 12;
insert newContract;
Order newOrder = new Order();
newOrder.AccountId = newAccount.Id;
newOrder.ContractId = newContract.Id;
newOrder.Status = 'Draft';
newOrder.EffectiveDate = Date.today();
insert newOrder;
}
}
// Test method to check the functionality of the OrderTrigger.
@isTest
static void checkLeadSourceField() {
// Get the ID of Marketing Team role.
String MarketingTeamroleId = [
SELECT Id
FROM UserRole
WHERE Name = 'Marketing Team'
LIMIT 1
].Id;
// Query the Order record to be updated.
Order orderToUpdate = [
SELECT hasDiscount__c, Total_order_price__c
FROM Order
WHERE Status = 'Draft'
];
// Get the Marketing Team user.
User userMarketingTeam = [
SELECT Id
FROM User
WHERE UserRoleId = :MarketingTeamroleId
LIMIT 1
];
// Run tests within the context of the Marketing Team user.
System.runAs(userMarketingTeam) {
// Update fields on the Order record and test the trigger logic.
orderToUpdate.hasDiscount__c = true;
orderToUpdate.Total_order_price__c = 2050;
Test.startTest();
update orderToUpdate;
Test.stopTest();
}
// Query the Order record after the update.
Order orderWithDiscount = [
SELECT hasDiscount__c, Total_order_price__c
FROM Order
WHERE Status = 'Draft'
];
// Verify that Total_order_price__c field was modified by the trigger
System.assertNotEquals(orderToUpdate.Total_order_price__c, orderWithDiscount.Total_order_price__c);
}
}
Тест для Account тригеру:
@isTest
public class AccountTriggerTest {
// Test setup method to create a new Account record.
@testSetup
static void testSetup() {
// Create a new Account record for testing.
Account newAccount = new Account();
newAccount.Name = 'TestAccount';
insert newAccount;
}
// Test method to check the functionality of the AccountTrigger.
@isTest
static void checkIndustryField() {
// Query the Account record to be updated.
Account accountToUpdate = [
SELECT Industry
FROM Account
WHERE Name = 'TestAccount'
];
// Start a test context to simulate an update operation on the Account record.
Test.startTest();
update accountToUpdate;
Test.stopTest();
// Query the updated Account record.
Account accountWithOtherIndustry = [
SELECT Industry
FROM Account
WHERE Name = 'TestAccount'
];
// Assert that the Industry field of the Account record was set to 'Other' after the update.
System.assertEquals('Other', accountWithOtherIndustry.Industry);
}
}
Як можна помітити, для тестування тригеру для Order нам потрібно створити чимало даних. А уявіть в рамках проекту, як можуть бути між собою пов’язані об’єкти та якими масштабними будуть testSetup для них, й скільки часу кожен розробник витратить на цю роботу. DataFactoty може значно скоротити й спростити все. Давайте розглянемо код нижче.
DataFactoty клас:
public class TestDataFactory {
// Constants to store Profile Id and Marketing Team Role Id.
private static final String ADMIN_PROFILE_ID = [
SELECT Id
FROM Profile
WHERE Name = 'System Administrator'
LIMIT 1
].Id;
private static final String MARKETING_TEAM_ROLE_ID = [
SELECT Id
FROM UserRole
WHERE Name = 'Marketing Team'
LIMIT 1
].Id;
// Method to create test data.
public static void createTestData() {
// Create a user with Marketing Team role.
User userMarketingTeam = new User(
Alias = 'adm',
Country = 'United Kingdom',
Email = 'adm.man@test.com',
EmailEncodingKey = 'UTF-8',
LastName = 'admin',
LanguageLocaleKey = 'en_US',
LocaleSidKey = 'en_US',
ProfileId = ADMIN_PROFILE_ID,
TimeZoneSidKey = 'America/Los_Angeles',
UserName = 'admin@test.tradingscreen.com',
UserRoleId = MARKETING_TEAM_ROLE_ID
);
insert userMarketingTeam;
// Run tests within the context of the Marketing Team user.
System.runAs(userMarketingTeam) {
// Create test records: Account, Contact, Contract, and Order.
Account newAccount = new Account();
newAccount.Name = 'TestAccount';
insert newAccount;
Contact newContact = new Contact();
newContact.FirstName = 'Joe';
newContact.LastName = 'Test';
insert newContact;
Contract newContract = new Contract();
newContract.Name = 'Test';
newContract.AccountId = newAccount.Id;
newContract.Status = 'Draft';
newContract.StartDate = Date.today();
newContract.ContractTerm = 12;
insert newContract;
Order newOrder = new Order();
newOrder.Name = 'Test';
newOrder.AccountId = newAccount.Id;
newOrder.ContractId = newContract.Id;
newOrder.Status = 'Draft';
newOrder.EffectiveDate = Date.today();
insert newOrder;
}
}
// Method to retrieve test data and organize it in a map for testing purposes.
public static Map<String, Map<String, SObject>> getTestData() {
Map<String, Map<String, SObject>> testData = new Map<String, Map<String, SObject>>();
// Retrieve and organize User, Account, Contact, Contract, and Order records.
testData.put('users', new Map<String, SObject>([
SELECT LastName, UserRoleId, Name
FROM User
]));
testData.put('accounts', new Map<String, SObject>([
SELECT Name, Industry
FROM Account
]));
testData.put('contacts', new Map<String, SObject>([
SELECT LastName, LeadSource
FROM Contact
]));
testData.put('contracts', new Map<String, SObject>([
SELECT Name
FROM Contract
]));
testData.put('orders', new Map<String, SObject>([
SELECT Name, hasDiscount__c, Total_order_price__c
FROM Order
]));
return testData;
}
}
І тепер тести можна переписати наступним чином. У наступних прикладах тестові дані будуть формуватися у класі TestDataFactory.
Тест для Order тригеру з використанням Data Factory:
@isTest
public class OrderTriggerTestDataFactory {
// Retrieve test data from TestDataFactory class.
private static final Map<String, Map<String, SObject>> TEST_DATA = TestDataFactory.getTestData();
// Test setup method to create necessary test data.
@testSetup
static void testSetup() {
// Call the createTestData method from TestDataFactory to create required test data.
TestDataFactory.createTestData();
}
// Test method to check the functionality of the OrderTrigger.
@isTest
static void checkLeadSourceField() {
// Retrieve the Order record to be updated from the test data.
Order orderToUpdate = (Order)TEST_DATA.get('orders').get('Test');
// Retrieve the user with Marketing Team role from the test data.
User userMarketingTeam = (User)TEST_DATA.get('users').get('admin');
// Run tests within the context of the Marketing Team user.
System.runAs(userMarketingTeam) {
// Update fields on the Order record.
orderToUpdate.hasDiscount__c = true;
orderToUpdate.Total_order_price__c = 2050;
// Start a test context to simulate an update operation on the Order record.
Test.startTest();
update orderToUpdate;
Test.stopTest();
}
// Query the Order record after the update.
Order orderWithDiscount = [
SELECT hasDiscount__c, Total_order_price__c
FROM Order
WHERE Status = 'Draft'
];
// Verify that Total_order_price__c field was modified by the trigger.
System.assertNotEquals(orderToUpdate.Total_order_price__c, orderWithDiscount.Total_order_price__c);
}
}
Та ми можемо використовувати ці дані у всіх тестах та розширювати Data Factory відповідно до наших потреб.
Тест для Account тригеру з використанням Data Factory:
@isTest
public class AccountTriggerTestDataFactory {
// Retrieve test data from TestDataFactory class.
private static final Map<String, Map<String, SObject>> TEST_DATA = TestDataFactory.getTestData();
// Test setup method to create necessary test data.
@testSetup
static void testSetup() {
// Call the createTestData method from TestDataFactory to create required test data.
TestDataFactory.createTestData();
}
// Test method to check the functionality of the AccountTrigger.
@isTest
static void checkIndustryField() {
// Retrieve the Account record to be updated from the test data.
Account accountToUpdate = (Account)TEST_DATA.get('accounts').get('TestAccount');
// Start a test context to simulate an update operation on the Account record.
Test.startTest();
update accountToUpdate;
Test.stopTest();
// Query the Account record after the update.
Account accountWithOtherIndustry = [
SELECT Industry
FROM Account
WHERE Name = 'TestAccount'
];
// Verify that the Industry field of the Account record was set to 'Other' after the update.
System.assertEquals('Other', accountWithOtherIndustry.Industry);
}
}
Отже, у цій статті було розглянуто важливий аспект тестування програмного забезпечення - генерацію тестових даних за допомогою підходу, відомого як Test Data Factory. На прикладі конкретної ситуації було продемонстровано, як він може бути використаний для створення реалістичних тестових сценаріїв.
Цей підхід не лише поліпшує якість тестування, але й заощаджує час та ресурси, які можуть бути витрачені на ручне створення тестових даних. Крім того, Test Data Factory може бути особливо корисний при тестуванні великих та складних систем, де необхідно проводити тестування великої кількості варіантів вхідних даних.