This Gist shows code examples for Apex Testing Best Practices. It relys on having the TestFactory in your org.
Last active
April 28, 2022 03:30
-
-
Save dhoechst/aa31d49bd6a961fe6b77 to your computer and use it in GitHub Desktop.
Testing Best Practices and Examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class MyApexClass { | |
// This method will be used to demonstrate a positive test | |
public static String doSomethingAwesome() { | |
return doSomethingAwesome(false); | |
} | |
// This method will be used to demostrate how to test a thrown exception | |
public static String doSomethingAwesome(Boolean throwException) { | |
if (throwException) { | |
throw new MyApexClassException('Something bad happened'); | |
} | |
return 'Awesome'; | |
} | |
// This method will be used to demonstrate how to test database queries | |
public static Account queryAccount() { | |
return [SELECT Name FROM Account LIMIT 1]; | |
} | |
// This annotation makes a normally private method visible to unit tests | |
@TestVisible | |
private static String privateAwesome() { | |
return 'Still Awesome'; | |
} | |
public class MyApexClassException extends Exception {} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest | |
private class MyApexClassTest { | |
// This test shows a postive path. | |
@isTest static void verifyAwesome() { | |
System.assertEquals('Awesome', MyApexClass.doSomethingAwesome(), 'The awesome method was not so awesome.'); | |
} | |
// This test shows how to cause an exception to be thrown and catch it. | |
@isTest static void exceptAwesome() { | |
try { | |
MyApexClass.doSomethingAwesome(true); | |
} catch (MyApexClass.MyApexClassException e) { | |
return; | |
} | |
System.assert(false, 'A MyApexClassException was expected, but not thrown.'); | |
} | |
// This test shows how to setup test data to be used in your methods. | |
@isTest static void queryAccountTest() { | |
Account a = (Account) TestFactory.createSObject(new Account()); | |
insert a; | |
Test.startTest(); // Resets all limits (DML, SOQL, etc) | |
Account testAccount = MyApexClass.queryAccount(); | |
Test.stopTest(); | |
System.assertEquals(a.Name, testAccount.Name, 'The wrong account was returned.'); | |
} | |
// This test is calling a private method decorated with @TestVisible | |
@isTest static void verifyPrivateAwesome() { | |
System.assertEquals('Still Awesome', MyApexClass.privateAwesome()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trigger DemoContactTrigger on Contact (before insert) { | |
// Bulk version | |
/* | |
List<Id> acctIds = new List<Id>(); | |
for (Contact c : Trigger.New) { | |
if (c.AccountId != null) { | |
acctIds.add(c.AccountId); | |
} | |
} | |
Map<Id, Account> acctMap = new Map<Id, Account>([SELECT Id, Account_Email__c FROM Account where Id in :acctIds]); | |
for (Contact c : Trigger.New) { | |
if (c.Email == null && c.AccountId != null) { | |
c.Email = acctMap.get(c.AccountId).Account_Email__c; | |
} | |
} | |
*/ | |
// Non bulk version | |
for (Contact c : Trigger.New) { | |
if (c.Email == null) { | |
//if (c.Email == null && c.AccountId != null) { | |
c.Email = [SELECT Account_Email__c FROM Account WHERE Id = :c.AccountId].Account_Email__c; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest | |
private class DemoContactTriggerTest { | |
@isTest static void insertSingleContactNoEmail() { | |
Account a = (Account) TestFactory.createSObject(new Account(Account_Email__c = '[email protected]')); | |
insert a; | |
Contact c = (Contact) TestFactory.createSObject(new Contact(AccountId = a.Id)); | |
Test.startTest(); | |
insert c; | |
Test.stopTest(); | |
Contact testContact = [SELECT Email from Contact where Id = :c.Id]; | |
System.assertEquals('[email protected]', testContact.Email, 'The contact email should have been changed to the account email.'); | |
} | |
@isTest static void insertSingleContactWithEmail() { | |
Account a = (Account) TestFactory.createSObject(new Account(Account_Email__c = '[email protected]')); | |
insert a; | |
Contact c = (Contact) TestFactory.createSObject(new Contact(AccountId = a.Id, Email = '[email protected]')); | |
Test.startTest(); | |
insert c; | |
Test.stopTest(); | |
Contact testContact = [SELECT Email from Contact where Id = :c.Id]; | |
System.assertEquals('[email protected]', testContact.Email, 'The contact email should not have been changed.'); | |
} | |
@isTest static void insertPrivateContact() { | |
Contact c = (Contact) TestFactory.createSObject(new Contact(Email = '[email protected]')); | |
Test.startTest(); | |
insert c; | |
Test.stopTest(); | |
Contact testContact = [SELECT Email from Contact where Id = :c.Id]; | |
System.assertEquals('[email protected]', testContact.Email, 'The contact email should not have been changed.'); | |
} | |
@isTest static void insertAccountNoEmail() { | |
Account a = (Account) TestFactory.createSObject(new Account()); | |
insert a; | |
Contact c = (Contact) TestFactory.createSObject(new Contact(AccountId = a.Id)); | |
Test.startTest(); | |
insert c; | |
Test.stopTest(); | |
Contact testContact = [SELECT Email from Contact where Id = :c.Id]; | |
System.assertEquals(null, testContact.Email, 'There shouldn\'t be an email on the contact.'); | |
} | |
@isTest static void insertBulkContact() { | |
Account a = (Account) TestFactory.createSObject(new Account(Account_Email__c = '[email protected]')); | |
insert a; | |
List<Contact> testContacts = (List<Contact>) TestFactory.createSObjectList(new Contact(AccountId = a.Id), 200); | |
Test.startTest(); | |
insert testContacts; | |
Test.stopTest(); | |
for (Contact c : [SELECT Email FROM Contact WHERE Id in :testContacts]) { | |
System.assertEquals('[email protected]', c.Email, 'The contact email should have been changed to the account email.'); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class CalloutClass { | |
public static String doCallout() { | |
Http h = new Http(); | |
HttpRequest req = new HttpRequest(); | |
req.setEndpoint('https://api.endpoint.com/something'); | |
req.setMethod('GET'); | |
HttpResponse res = h.send(req); | |
return res.getBody(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest | |
global class MockHttpResponse implements HttpCalloutMock { | |
global HTTPResponse respond(HTTPRequest req) { | |
HttpResponse res = new HttpResponse(); | |
res.setHeader('Content-Type', 'application/json'); | |
res.setStatusCode(200); | |
res.setBody('A response'); | |
return res; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest | |
private class CalloutClassTest { | |
@isTest static void testCallOut() { | |
Account a = (Account) TestFactory.createSObject(new Account()); | |
insert a; | |
// We need to use startTest() to avoid an uncommitted work pending exception | |
Test.startTest(); | |
Test.setMock(HttpCalloutMock.class, new MockHttpResponse()); | |
String response = CalloutClass.doCallout(); | |
Test.stopTest(); | |
System.assertEquals('A response', response); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class DemoControllerExtension { | |
private final Account acct; | |
public DemoControllerExtension(ApexPages.StandardController stdController) { | |
this.acct = (Account)stdController.getRecord(); | |
} | |
public String param { | |
get { | |
if (param == null) { | |
param = ApexPages.currentPage().getParameters().get('param'); | |
} | |
return param; | |
} | |
set; | |
} | |
public PageReference doSomething() { | |
if (param == null) { | |
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'No param provided')); | |
} | |
return null; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<apex:page standardController="Account" extensions="DemoControllerExtension"> | |
<apex:pageMessages /> | |
<apex:form> | |
<apex:pageBlock> | |
<apex:pageBlockButtons> | |
<apex:commandButton action="{!doSomething}" value="Do Something" /> | |
</apex:pageBlockButtons> | |
<apex:pageBlockSection columns="1"> | |
<apex:outputField value="{!Account.Name}" /> | |
<apex:pageBlockSectionItem> | |
<apex:outputLabel value="Parameter" /> | |
<apex:outputText value="{!param}" /> | |
</apex:pageBlockSectionItem> | |
</apex:pageBlockSection> | |
</apex:pageBlock> | |
</apex:form> | |
</apex:page> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest | |
private class DemoControllerExtensionTest { | |
private static Account testAccount; | |
static { | |
testAccount = (Account) TestFactory.createSObject(new Account()); | |
insert testAccount; | |
} | |
@isTest static void checkErrorNoParam() { | |
DemoControllerExtension testCtrl = new DemoControllerExtension(new ApexPages.StandardController(testAccount)); | |
testCtrl.doSomething(); | |
System.assert(!ApexPages.getMessages().isEmpty(), 'There should be an error displayed on the page that no parameter was given.'); | |
} | |
@isTest static void checkParam() { | |
ApexPages.CurrentPage().getParameters().put('param', testAccount.name); | |
DemoControllerExtension testCtrl = new DemoControllerExtension(new ApexPages.StandardController(testAccount)); | |
testCtrl.doSomething(); | |
System.assertEquals(testAccount.name, testCtrl.param); | |
System.assert(ApexPages.getMessages().isEmpty(), 'There should be no errors displayed on the page.'); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest | |
private class RunAsTest { | |
@isTest static void mixedDml() { | |
Profile p = [select id from profile where name='Standard User']; | |
User u = new User(alias = 'standt', email='[email protected]', | |
emailencodingkey='UTF-8', lastname='Testing', | |
languagelocalekey='en_US', | |
localesidkey='en_US', profileid = p.Id, | |
timezonesidkey='America/Los_Angeles', | |
username='[email protected]'); | |
insert u; | |
Group g = new Group(); | |
g.Name = 'Test Group'; | |
g.DeveloperName = 'TestGroup'; | |
insert g; | |
GroupMember gm = new GroupMember(); | |
gm.GroupId = g.Id; | |
gm.UserOrGroupId = u.Id; | |
insert gm; | |
// If you don't use runAs here, you will get a mixed DML exception | |
System.runAs(u) { | |
Account a = new Account(); | |
a.Name = 'Test Account'; | |
insert a; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment