Apex ํ ์คํธ์ฉ ํ ์คํธ ๋ฐ์ดํฐ ์์ฑ
ํ์ต ๋ชฉํ
์ด ์ ๋์ ์๋ฃํ๋ฉด ๋ค์์ ์ํํ ์ ์์ต๋๋ค.
- ํ
์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
- ํ
์คํธ ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ํ
์คํธ ์ผ์ด์ค์ ๋ํ ํ
์คํธ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
- ํด๋์ค์ ๋ชจ๋ ํ
์คํธ ๋ฉ์๋๋ฅผ ์คํํ ์ ์์ต๋๋ค.
- ํ
์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ
์คํธ ๋ฐ์ดํฐ ์ค์ ์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค.
์ ์ ์กฐ๊ฑด
์์ง ์ํํ์ง ์์ ๊ฒฝ์ฐ ์ด์ ์ ๋ Apex ํธ๋ฆฌ๊ฑฐ ํ ์คํธ์ ์ ์ ์กฐ๊ฑด์ ์๋ฃํฉ๋๋ค.
ํ ์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค ์ถ๊ฐ
ํ ์คํธ ๋ฐ์ดํฐ ์์ฑ์ ์ ํธ๋ฆฌํฐ ํด๋์ค ๋ฉ์๋์ ๋ํ ํธ์ถ๋ก ๋์ฒดํ์ฌ ์ด์ ํ ์คํธ ๋ฉ์๋๋ฅผ ๋ฆฌํฉํ ๋งํด ๋ณด๊ฒ ์ต๋๋ค. ๋จผ์ ํ ์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
TestDataFactory ํด๋์ค๋ @IsTest๋ผ๊ณ ์ฃผ์์ด ์ถ๊ฐ๋ ๊ณต๊ฐ ํด๋์ค์ด๋ฉฐ, ์คํ ์ค์ธ ํ
์คํธ์์๋ง ์ก์ธ์คํ ์ ์์ต๋๋ค. ํ
์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค์๋ ํ
์คํธ ๋ฐ์ดํฐ ์ค์ ๊ณผ ๊ฐ์ ์ ์ฉํ ์์
์ ์ํํ๊ธฐ ์ํด ํ
์คํธ ๋ฉ์๋์์ ํธ์ถํ ์ ์๋ ๋ฉ์๋๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ํ
์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ ์กฐ์ง์ ์ฝ๋ ํฌ๊ธฐ ์ ํ์์ ์ ์ธ๋ฉ๋๋ค.
TestDataFactory ํด๋์ค์ ์ถ๊ฐํ๋ ค๋ฉด ๋ค์ ๋จ๊ณ๋ฅผ ์ํํ์ธ์.
- Developer Console์์ File(ํ์ผ) | New(์๋ก ๋ง๋ค๊ธฐ) | Apex Class(Apex ํด๋์ค)๋ฅผ ํด๋ฆญํ๊ณ ํด๋์ค ์ด๋ฆ์ ๋ํด
TestDataFactory๋ฅผ ์ ๋ ฅํ ๋ค์ OK(ํ์ธ)๋ฅผ ํด๋ฆญํฉ๋๋ค. - ๊ธฐ๋ณธ ํด๋์ค ๋ณธ๋ฌธ์ ๋ค์์ผ๋ก ๋ฐ๊ฟ๋๋ค.
@IsTest public with sharing class TestDataFactory { public static List<Account> createAccountsWithOpps(Integer numAccts, Integer numOppsPerAcct) { List<Account> accts = new List<Account>(); for(Integer i=0;i<numAccts;i++) { Account a = new Account(Name='TestAccount' + i); accts.add(a); } insert as user accts; List<Opportunity> opps = new List<Opportunity>(); for(Integer j=0;j<numAccts;j++) { Account acct = accts[j]; // For each account just inserted, add opportunities for(Integer k=0;k<numOppsPerAcct;k++) { opps.add(new Opportunity(Name=acct.Name + ' Opportunity ' + k, StageName='Prospecting', CloseDate=System.today().addMonths(1), AccountId=acct.Id)); } } // Insert all opportunities for all accounts. insert as user opps; return accts; } }
์ด ํ
์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค์๋ ํ๋์ ์ ์ ๋ฉ์๋ createAccountsWithOpps()๊ฐ ํฌํจ๋์ด ์์ผ๋ฉฐ, ๊ณ์ ๊ฐ์(numAccts ๋งค๊ฐ๋ณ์) ๋ฐ ๊ฐ ๊ณ์ ์ ๋ํด ์์ฑํ ๊ด๋ จ ๊ธฐํ ๊ฐ์(numOppsPerAcct ๋งค๊ฐ๋ณ์)๋ฅผ ํ์ฉํฉ๋๋ค. ๋ฉ์๋์ ์ฒซ ๋ฒ์งธ ๋ฃจํ๋ ์ง์ ๋ ์์ ๊ณ์ ์ ์์ฑํ๊ณ accts ๋ชฉ๋ก ๋ณ์์์ ์ด๋ฌํ ๊ณ์ ์ ์ ์ฅํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ๋ฃจํ ํ, insert DML ๋ฌธ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ชฉ๋ก์ ์๋ ๋ชจ๋ ๊ณ์ ์ ์์ฑํ๊ธฐ ์ํด ํธ์ถ๋ฉ๋๋ค.
๋ ๋ฒ์งธ ๋ฃจํ๋ ๊ธฐํ๋ฅผ ๋ง๋ญ๋๋ค. ๊ฐ ๊ธฐํ ๊ทธ๋ฃน์ด ํ๋์ ๊ณ์ ์ ์ฐ๊ฒฐ๋์ด ์์ผ๋ฏ๋ก ์ธ๋ถ ๋ฃจํ๋ ๊ณ์ ์ ๋ฐ๋ณตํ๊ณ ํ์ฌ ๊ณ์ ์ ๋ํ ๊ด๋ จ ๊ธฐํ๋ฅผ ์์ฑํ๋ ์ค์ฒฉ ๋ฃจํ๋ฅผ ํฌํจํฉ๋๋ค. ๋ค์์ ์ค์ฒฉ ๋ฃจํ๊ฐ ์คํ๋ ๋ ๊ธฐํ๋ add() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋์ผํ ๋ชฉ๋ก์ ์ถ๊ฐ๋ฉ๋๋ค. ๊ธฐํ๋ AccountId ํ๋๋ฅผ ์ฌ์ฉํ์ฌ ์์ ๊ณ์ ์ ์ฐ๊ฒฐ๋ฉ๋๋ค. ์์ฑ๋ ๋ชจ๋ ๊ธฐํ์ ์ด ์๋ ๊ธฐํ ๊ฐ์์ ๊ณ์ ๊ฐ์(numOppsPerAcct*numAccts์ ์ ๊ณฑ๊ฐ์
๋๋ค. ๋ค์์ผ๋ก, insert DML ๋ฌธ์ ๋ฃจํ ์ธ๋ถ์์ ํจ์จ์ ์ผ๋ก ํธ์ถ๋์ด ํธ์ถ ํ ๋ฒ์ผ๋ก ๋ชจ๋ ๊ณ์ ์ ๋ํ ์ปฌ๋ ์
์ ๋ชจ๋ ๊ธฐํ๋ฅผ ์์ฑํฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก ์ด ๋ฉ์๋๋ ์ ๊ณ์ ๋ชฉ๋ก์ ๋ฐํํฉ๋๋ค.
ํ ์คํธ ๋ฐ์ดํฐ ์์ฑ์ ์ํ ์ ํธ๋ฆฌํฐ ๋ฉ์๋ ํธ์ถ
์ด์ ํ
์คํธ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ์ถ๊ฐํ์ผ๋ฏ๋ก ์ด ํด๋์ค๋ฅผ ํ์ฉํ๋๋ก TestAccountDeletion ํ
์คํธ ํด๋์ค๋ฅผ ์์ ํฉ๋๋ค. TestDataFactory.createAccountsWithOpps(1,1) ํธ์ถ์ ์ํด ๋ฐํ๋ ๋ฐฐ์ด์๋ ํ๋์ Account sObject๊ฐ ํฌํจ๋ฉ๋๋ค.
๋ค์์ ์์ ๋ ํ ์คํธ ๋ฉ์๋์ ๋๋ค.
@IsTest
private with sharing class TestAccountDeletion {
@IsTest
static void TestDeleteAccountWithOneOpportunity() {
// Test data setup
// Create one account with one opportunity by calling a utility method
Account[] accts = TestDataFactory.createAccountsWithOpps(1,1);
// Perform test
Test.startTest();
Database.DeleteResult result = Database.delete(accts[0], false, AccessLevel.USER_MODE);
Test.stopTest();
// Verify that the deletion should have been stopped by the trigger,
// so check that we got back an error.
Assert.isFalse(result.isSuccess());
Assert.isTrue(result.getErrors().size() > 0);
Assert.areEqual('Cannot delete account with related opportunities.',
result.getErrors()[0].getMessage());
}
}๋ค์ํ ์กฐ๊ฑด์ ๋ํ ํ ์คํธ
ํ๋์ ํ ์คํธ ๋ฉ์๋๋ก๋ ํธ๋ฆฌ๊ฑฐ์ ๋ํด ๊ฐ๋ฅํ ๋ชจ๋ ์ ๋ ฅ์ ํ ์คํธํ๊ธฐ์ ์ถฉ๋ถํ์ง ์์ต๋๋ค. ๊ธฐํ๊ฐ ์๋ ๊ณ์ ์ด ์ญ์ ๋๋ ๊ฒฝ์ฐ์ ๊ฐ์ ๋ช ๊ฐ์ง ๋ค๋ฅธ ์กฐ๊ฑด์ ํ ์คํธํด์ผ ํฉ๋๋ค. ๋ํ ๋จ์ผ ๋ ์ฝ๋ ๋์ ๋๋ ๋ ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ๋์ผํ ์๋๋ฆฌ์ค๋ฅผ ํ ์คํธํด์ผ ํฉ๋๋ค. ๋ค์์ ์ธ ๊ฐ์ง ์ถ๊ฐ ํ ์คํธ ๋ฉ์๋๊ฐ ํฌํจ๋ ํ ์คํธ ํด๋์ค์ ์ ๋ฐ์ดํธ๋ ๋ฒ์ ์ ๋๋ค. ์ด ์ ๋ฐ์ดํธ๋ ๋ฒ์ ์ ํด๋์ค๋ฅผ ์ ์ฅํฉ๋๋ค.
@IsTest
private with sharing class TestAccountDeletion {
@IsTest
static void TestDeleteAccountWithOneOpportunity() {
// Test data setup
// Create one account with one opportunity by calling a utility method
Account[] accts = TestDataFactory.createAccountsWithOpps(1,1);
// Perform test
Test.startTest();
Database.DeleteResult result = Database.delete(accts[0], false, AccessLevel.USER_MODE);
Test.stopTest();
// Verify that the deletion should have been stopped by the trigger,
// so check that we got back an error.
Assert.isFalse(result.isSuccess());
Assert.isTrue(result.getErrors().size() > 0);
Assert.areEqual('Cannot delete account with related opportunities.',
result.getErrors()[0].getMessage());
}
@IsTest
static void TestDeleteAccountWithNoOpportunities() {
// Test data setup
// Create one account with no opportunities by calling a utility method
Account[] accts = TestDataFactory.createAccountsWithOpps(1,0);
// Perform test
Test.startTest();
Database.DeleteResult result = Database.delete(accts[0], false, AccessLevel.USER_MODE);
Test.stopTest();
// Verify that the deletion was successful
Assert.isTrue(result.isSuccess());
}
@IsTest
static void TestDeleteBulkAccountsWithOneOpportunity() {
// Test data setup
// Create accounts with one opportunity each by calling a utility method
Account[] accts = TestDataFactory.createAccountsWithOpps(200,1);
// Perform test
Test.startTest();
Database.DeleteResult[] results = Database.delete(accts, false, AccessLevel.USER_MODE);
Test.stopTest();
// Verify for each record.
// In this case the deletion should have been stopped by the trigger,
// so check that we got back an error.
for(Database.DeleteResult dr : results) {
Assert.isFalse(dr.isSuccess());
Assert.isTrue(dr.getErrors().size() > 0);
Assert.areEqual('Cannot delete account with related opportunities.',
dr.getErrors()[0].getMessage());
}
}
@IsTest
static void TestDeleteBulkAccountsWithNoOpportunities() {
// Test data setup
// Create accounts with no opportunities by calling a utility method
Account[] accts = TestDataFactory.createAccountsWithOpps(200,0);
// Perform test
Test.startTest();
Database.DeleteResult[] results = Database.delete(accts, false, AccessLevel.USER_MODE);
Test.stopTest();
// For each record, verify that the deletion was successful
for(Database.DeleteResult dr : results) {
Assert.isTrue(dr.isSuccess());
}
}
}๋ชจ๋ ํ ์คํธ ๋ฉ์๋ ์คํ
๋ง์ง๋ง ๋จ๊ณ๋ ํ
์คํธ ํด๋์ค์์ ํ
์คํธ ๋ฉ์๋๋ฅผ ์คํํ๋ฉฐ, ์ด์ ํด๋์ค์ ๋ ํฌ๊ด์ ์ธ ํ
์คํธ๊ฐ ํฌํจ๋๊ณ ํ
์คํธ ๋ฐ์ดํฐ ํฉํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๋ก ๋ฆฌํฉํ ๋ง๋์์ต๋๋ค. TestAccountDeletion ํด๋์ค์์ ์ด๋ฏธ ํ
์คํธ๋ฅผ ์คํํ์ผ๋ฏ๋ก ์ด ํ
์คํธ ํด๋์ค๋ฅผ ๋ค์ ์คํํ์ฌ ๋ชจ๋ ํ
์คํธ ๋ฉ์๋๋ฅผ ์คํํ ์ ์์ต๋๋ค.
- ๋์ผํ ํ
์คํธ ์คํ์ ์คํํ๋ ค๋ฉด Tests(ํ
์คํธ) ํญ์ ํด๋ฆญํ๊ณ ํ
์คํธ ์คํ์ ์ ํํ ๋ค์ Test(ํ
์คํธ) | Rerun(์ฌ์คํ)์ ํด๋ฆญํฉ๋๋ค.
- ์ต์ ํ
์คํธ ์คํ์ ํ์ฅํ์ฌ Tests(ํ
์คํธ) ํญ์์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํฉ๋๋ค. ํ
์คํธ ์คํ์ ๋ค ๊ฐ์ง ํ
์คํธ๊ฐ ๋ชจ๋ ํต๊ณผ๋์์์ ๋ณด๊ณ ํด์ผ ํฉ๋๋ค.
๋ฆฌ์์ค
