Three principles of Apex testing are provided:
1. Use assertions to validate expected behavior and outputs. Every test method should include at least one assertion.
2. Use Test.startTest() and Test.stopTest() to isolate code from setup and ensure governor limits are hit.
3. Write both positive and negative tests. Positive tests validate expected behavior, while negative tests validate exceptions are properly handled for invalid inputs.
3. Safe Harbor
Safe harbor statement under the Private Securities Litigation Reform Act of 1995:
This presentation may contain forward-looking statements that involve risks, uncertainties, and assumptions. If any such uncertainties materialize or
if any of the assumptions proves incorrect, the results of salesforce.com, inc. could differ materially from the results expressed or implied by the
forward-looking statements we make. All statements other than statements of historical fact could be deemed forward-looking, including any
projections of product or service availability, subscriber growth, earnings, revenues, or other financial items and any statements regarding strategies
or plans of management for future operations, statements of belief, any statements concerning new, planned, or upgraded services or technology
developments and customer contracts or use of our services.
The risks and uncertainties referred to above include – but are not limited to – risks associated with developing and delivering new functionality for
our service, new products and services, our new business model, our past operating losses, possible fluctuations in our operating results and rate of
growth, interruptions or delays in our Web hosting, breach of our security measures, the outcome of any litigation, risks associated with completed
and any possible mergers and acquisitions, the immature market in which we operate, our relatively limited operating history, our ability to expand,
retain, and motivate our employees and manage our growth, new releases of our service and successful customer deployment, our limited history
reselling non-salesforce.com products, and utilization and selling to larger enterprise customers. Further information on potential factors that could
affect the financial results of salesforce.com, inc. is included in our annual report on Form 10-K for the most recent fiscal year and in our quarterly
report on Form 10-Q for the most recent fiscal quarter. These documents and others containing important disclosures are available on the SEC
Filings section of the Investor Information section of our Web site.
Any unreleased services or features referenced in this or other presentations, press releases or public statements are not currently available and
may not be delivered on time or at all. Customers who purchase our services should make the purchase decisions based upon features that are
currently available. Salesforce.com, inc. assumes no obligation and does not intend to update these forward-looking statements.
4. Agenda
Why we’re here
Lets talk about why we write tests
10 principles of testing Apex Code
A brief note on Mocking (Not the making fun of things kind)
Questions and Answers
• Note: If you don’t ask questions, I’ll have to ask you some.
5. I found this comment in an org
once.
In a “test” class full of carefully
crafted additions.
i++;
i++;
i++;
…
System.assert(i = 1000);
6. Lets talk about why we test.
The real reasons
Tests provide assurance of functionality
Tests reduce cost of change
Tests encourage modular, reusable code
Tests help identify engineering and architectural bugs
Tests help document expected behavior
Tests + Code = less likely to produce bugs
7. Principle #0 also known as: the general idea
A recipe for successful unit tests in Apex code
Assertions
StopTest()
Execute Your Unit of Code
StartTest()
Create Test Data
Test Method
8. Principle #1, Use Asserts
A test without Assert methods isn’t a test, it’s a liability.
Three Assert methods built-in
• System.Assert(boolean-expression, ‘friendly message’)
• System.AssertEquals(expect, actual, ‘friendly message’)
• System.AssertNotEquals(expected, actual, ‘friendly message)
Every test method should include at least one assertion.
Good test methods include more than one.
The most robust, and helpful test methods include two sets of asserts.
• Assert that you properly setup all your test data
• Assert that the given test data was properly mutated by your code.
9. Principle #1, Use Asserts
Top Tester Top Tip: You can write your own assert methods!
@isTest
public class customAssertions{
public class customAssertionException extends exception{}
public Boolean AssertDuplicateAccount(Account a, Account b){
// … compare accounts
if(duplicate != true) {
Throw new customAssertionException(‘whoa, it’s not the
same’);
}
return true; // Return true, without exception when assertion is true.
}
}
10. Principle #2, use startTest and stopTest
Start (then) Stop, Collaborate and Listen
Test.startTest() and Test.stopTest() help facilitate testing.
Isolates your code from the proper setup of the test(s)
startTest() resets DML, CPU Time and other governor limits, ensuring any limits you hit come from
your tested code!
stopTest() forces asynchronous code to complete.
11. Principle #3 How to test: Positive Tests
I believe you can do it!
You should write ‘Positive Tests’.
Positive tests prove the expected behavior, which is to say
they prove it does what you think it does
Lets talk about multiple expected behaviors, (what’s that
smell?)
Not just happy path testing
Given
Valid
Input
Executed
Code
Should
Return
Expected
Output
Use Assertions to
prove
12. Principle #3, Positive Tests
Public class exampleCode {
Public Integer add(Integer one, Integer two){
return one + two;
}
}
14. Principle #4, Negative Tests
It’s not you, it’s the *other drivers out there* that I don’t trust.
Negative tests prove that your code properly
handles exceptions and errors.
The pattern works by calling method within a
try/catch block in the test. Catch only the
expected type of exception in the catch block
Less intuitive but more powerful!
At the very least, negatively test methods that
utilize user data
Try {
Execute
Method
w/ Bad
Input
} Catch ()
{
didCatch
= true;
Boolean
didCatch =
false;
System.ass
ert(didCatch
)
15. Principle #4, Negative Tests
Public class exampleCode {
Public class exampleCodeException{}
Public Static Integer division(Integer one, Integer two){
if(two == 0) {
Throw new exampleCodeException(‘Dividing by zero makes
kittens cry’);
}
return one / two;
}
}
17. Principle #5, User Tests
Because really, do we want to give the HR team access to Products and
Opportunities?
User based tests prove your security model
works like you think it does.
Test with Users of different Roles / Profiles
and Permission sets!
The pattern works like this: Create a user with
a given profile. As needed assign permission
sets. Test both positive and negative users.
User
Role 1
Should have
access
User
Role 2
NO access
User w/
Perm
Set
No Access
before Perm
Set applied
User
w/o
Perm
Set
No Acces
18. Principle #5, User Tests
Public class exampleCode {
Public class exampleCodeException{}
Public Integer getBankAccount(Account a){
return a.SuperSekr3tBankAccountNum__c;
}
}
19. Principle #5, Positive User Tests
private class exampleCode_Tests {
@isTest static void test_getBankAccount_Positive() {
exampleCode drWho = new exampleCode();
User u = AwesomeTestLib.getUserWithProfile(‘JediProfile’);
Account a = (Account)TestFactory.createSObject(new Account());
Integer result;
System.runAs(u){
Test.startTest();
result = drWho.getBankAccount(a);
Test.stopTest();
}
System.assertNotEquals(result, null,
‘Expected The Doctor to have access to bank #’);
}
20. Principle #5, Negative User Tests
private class exampleCode_Tests {
@isTest static void test_getBankAccount_UberForNope() {
exampleCode Dalek = new exampleCode();
User u = AwesomeTestLib.getUserWithProfile(‘DalekProfile’);
Account a = (Account)TestFactory.createSObject(new Account());
Integer result;
System.runAs(u){
Test.startTest();
result = Dalek.getBankAccount(a);
Test.stopTest();
}
System.assertEquals(result, null, ‘Expected Daleks to be blocked’);
}
21. Principle #5, Testing with Perm Sets
private class exampleCode_Tests {
@isTest static void test_getBankAccount_W_PermSet() {
exampleCode ClaraOswald= new exampleCode();
User u = AwesomeTestLib.getUserWithProfile(‘Standard User’);
UtilityClass.AssignUserToPermissionSet(u, ‘Companion’);
Account a = (Account)TestFactory.createSObject(new Account());
Boolean result;
System.runAs(u){
Test.startTest();
result = ClaraOswald.canAccessTardis(a);
Test.stopTest();
}
System.assertNotEquals(result, null,
‘Expected ClaraOswald who has Companion Permissions to have access to the Tardis’);
}
22. Principle #6, Use your own data
Because, someone will delete the account named “do not delete, used for testing”
Always build your own test data.
If you created the data, you can precisely assert against it.
Unless you have to, never use @isTest(seeAllData=true)
List of Acceptable Reasons to use @seeAllData = true.
Reason Comments
You don’t know any better. Now you know better.
You’re unit testing Approval Processes Yeah. Sometimes you’re the statue, sometimes you’re the
pigeon.
??? ‘been spending most our lives living in a coder’s paradise
Used ‘seeAllData’ once or twice, living in a coder’s
paradise. Wrote a test on Monday, soon I’ll write a ‘nother
23. Principle #6, Use your own data
Work smarter, not harder.
• TestFactory, An open source test data factory from Daniel Hoechst. The coolest thing since the iPad. Found at
http://bit.ly/1c5exnV
• With it you can say:
• Account a = (Account)TestFactory.createSObject(new Account());
• Opportunity o = (Opportunity)TestFactory.createSObject(new Opportunity(AccountId =
a.Id));
• Account[] aList = (Account[])TestFactory.createSObjectList(new Account(), 200);
24. Principle #7, Use a domain specific helper lib
You know, so you’re not constantly reinventing the wheel.
Use a sane naming convention. Ie: Get* returns generated test data matching the method name!
Mark your test helper class as @isTest to ensure it’s never called by live code.
Amazing Candidates for test helper libs Comments
getStandardUserWithPermissionSet() Extend TestFactory.
getOpportunityWithAccountAndProducts() Generating complex object chains!
Any big of code you reuse in more than one test Because. Because. DRY. DRY.
HTTPMockGenerator Because having to include all those mock methods
gets boring.
25. Principle #8, Mocking
Go ahead, mock me.
Integration v. Unit tests
• Integration tests test code in an execution
context – i.e. setup some data and execute the
code within the context.
• Unit tests focus on a single unit of code, not
necessarily a complete function.
Mocks allow us to write true unit tests by
‘mock’ objects.
• You get to setup the mock, and it’s responses.
26. Principle #8, Mocking
The irritation of integration “unit” tests
Ordinary Unit Test
Calls a service
Returns ???
27. Principle #8, Mocking
In Soviet Russia Apex unit tests Mock you.
Unit Test w/ Mocks
Calls a mocked service
Returns exactly what you
asked it to.
28. Principle #8, Mocking, how it works
Marvelous Mock Objects Mocking Service Layer Objects
Data Access
Layer
Service LayerClasses
Unit Tests
Mock Service
Layer Objects
Bypassed
Standard
Service
Objects
Functions
Normally
29. Principle #8, Do’s, Don’ts and Dang-its of Mocking
It’s not just for Teenagers anymore.
Do mock objects or method whenever it may fail and throw off the test.
Do mock all your HTTP(s) callouts. Even those soapy things.
Do mock expensive service layer calls with abandon.
Do not mock the unit of code you’re testing at the moment. (Yes, I’ve seen this.)
You have to:
Use the excellent and amazing Fflib_ApexMocks.
Code to interfaces so you can Mock it good.
Ask yourself this: Does the code your testing depend on this object or
method having a specified value? If so, you must mock it. Mock it good.
30. Principle #8, Mocking for Mortals.
Public Boolean MockExample(Account a, Account b){
fflib_apexMocks m = new ffLib_apexMocks();
myInterface mock = new MockMyService(m);
m.startStubbing();
m.when(mock.add(5,7)).thenReturn(12);
m.stopStubbing();
Test.startTest();
ExampleCode mockObj = new exampleCode(myAwesomeService);
Integer result = mockObj.add(5,7);
Test.stopTest();
System.assertEquals(12, result, ‘friendly message’);
}
Keep calm and mock
31. Principle #8, Mocking Part 2 – those pesky http requests
Public class HTTPMockFactory implements HttpCalloutMock {
HTTPMockFactory (Integer code, String status, String body, Map<String, String> responseHeaders) {
//set class variables here.
}
public HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(this.code);
res.setStatus(this.status);
res.setBody(this.bodyAsString);
return res;
}
}
Your HTTP request is so fat, even Google couldn’t store it all.
32. Principle #9, Write for testing
Smaller methods are cute like kittens.
Write small, tightly focused methods that play well with others.
Compose advanced functionality by using your small methods together
Write more-or-less unit tests with these focused methods
Write true unit tests with mocks for testing custom, complex composed from your small, tightly
focused methods.
33. Principle #9, Write for testing: guidelines
You can thank me later.
Keep your methods to no more than 20 lines.
Keep method arguments to a minimum – no more than four.
Write your Visualforce controllers to use a single wrapper object.
• Either mock this wrapper object, or write a Test Helper method to construct it.
Use descriptive method names. Because the person who maintains this code after you’re long gone
may just be that psychopathic developer who knows where you live.
34. Principle #10, Use Continuous Integration!
I can haz Ci?
Continuous Integration is the process that enforces a full and complete test run is triggered every time
someone commit to your source repository. You are using Git right?
A number of tools are available for CI on the Salesforce1 platform.
Travis.ci, Drone.io, Jenkins, Bamboo and Codeship are some examples
35. Principle #10, Use Continuous Integration!
We need a process for this process.
CI gives you insight to failing tests as soon as they’re committed not when you try to deploy
Facilitates multiple developers working on the same project in their own sandboxes / dev orgs
Keeps you aware of code coverage as development occurs.
Groot'sdevsandbox
Groot
commits, CI
runs all the
tests
TestsPass!
Groot
makes a
pull request
Rocketreviews
Merges
code into
the mainline
CIrunstests
Testing the
integration
of these
changes
with others'
work
Notifications
Everyone
knows how
awesome
Groot is.
Deployment
issue is
created.
36. Principle #10, Use Continuous Integration!
Dude, you broke the build.
When tests fail… Sad Manager Panda is sad.
Groot'sdevsandbox
Groot
commits,
CI runs all
the tests
TestsFAIL!
Groot fixes
his bug
hoping no
one sees
Starlordreviews
Merges
code into
the
mainline
Shame on
him
CIrunstests
Groot’s
Change
breaks
Rocket’s
new
feature.
Notifications
Everyone
knows how
#FAIL
Groot is
Manager
Panda
eats Groot
38. Useful tips
Don’t insert or query unless you have to. You can often test with objects in memory!
STOP, COLLABORATE AND LISTEN, Jesse’s back with a brand new mission. Learn more about
Mocking by watching Jesse Altman’s (@JesseAltman) excellent intro to mocking with Apex mocks
here:
http://bit.ly/1HtXk2B
Write and use a standardized Logging library that wraps log data in a highly visible header/footer
GO HERE NOW GO HERE NOW
39. Share Your Feedback, and Win a GoPro!
3
Earn a GoPro prize entry for
each completed survey
Tap the bell to take a survey2Enroll in a session1