SlideShare a Scribd company logo
10 Principles of Apex Testing
Kevin Poorman. Sr. Customer Success Architect, Marketing Cloud.
Dreamforce 2015
Kevin Poorman
Kevin Poorman
Sr. Customer Success Architect
Salesforce Marketing Cloud
Twitter: @Codefriar
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.
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.
I found this comment in an org
once.
In a “test” class full of carefully
crafted additions.
i++;
i++;
i++;
…
System.assert(i = 1000);
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
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
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.
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.
}
}
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.
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
Principle #3, Positive Tests
Public class exampleCode {
Public Integer add(Integer one, Integer two){
return one + two;
}
}
Principle #3, Positive Tests
private class exampleCode_Tests {
@isTest static void test_Add_Postive() {
exampleCode drWho = new exampleCode();
Test.startTest();
Integer testValue = drWho.add(5,7);
Test.stopTest();
System.assertEquals(12, testValue,
‘Expected 5+7 to equal 12’);
}
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
)
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;
}
}
Principle #4, Negative Tests
private class exampleCode_Tests {
@isTest static void test_Divide_Negative() {
Boolean didCatchProperException = false;
Test.startTest();
Try {
exampleCode.divide(1, 0);
} catch (exampleCodeException AwesomeException){
didCatchProperException = true;
}
Test.stopTest();
System.assert(didCatchProperException,
‘Properly caught custom Exception’);
}
Kittens are
crying.
Bad Data. No
donut.
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
Principle #5, User Tests
Public class exampleCode {
Public class exampleCodeException{}
Public Integer getBankAccount(Account a){
return a.SuperSekr3tBankAccountNum__c;
}
}
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 #’);
}
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’);
}
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’);
}
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
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);
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.
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.
Principle #8, Mocking
The irritation of integration “unit” tests
Ordinary Unit Test
Calls a service
Returns ???
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.
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
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.
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
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.
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.
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.
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
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.
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
Thank you
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
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
10 Principles of Apex Testing

More Related Content

What's hot

Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...
Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...
Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...Ievgenii Katsan
 
Unit Testing Done Right
Unit Testing Done RightUnit Testing Done Right
Unit Testing Done RightBrian Fenton
 
Testers Desk Presentation
Testers Desk PresentationTesters Desk Presentation
Testers Desk PresentationQuality Testing
 
Unit testing best practices with JUnit
Unit testing best practices with JUnitUnit testing best practices with JUnit
Unit testing best practices with JUnitinTwentyEight Minutes
 
Dev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdetDev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdetdevlabsalliance
 
Test Classes in Salesforce
Test Classes in SalesforceTest Classes in Salesforce
Test Classes in SalesforceAtul Gupta(8X)
 
How to write effective test cases
How to write effective test casesHow to write effective test cases
How to write effective test casesHarshad Ingle
 
What is the decision rule all about?
What is the decision rule all about?What is the decision rule all about?
What is the decision rule all about?Maryam Sourani
 

What's hot (10)

Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...
Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...
Volodymyr Prymakov and Vlada Benyukh Detailed manual estimation approach for ...
 
Unit Testing Done Right
Unit Testing Done RightUnit Testing Done Right
Unit Testing Done Right
 
Testers Desk Presentation
Testers Desk PresentationTesters Desk Presentation
Testers Desk Presentation
 
Unit testing best practices with JUnit
Unit testing best practices with JUnitUnit testing best practices with JUnit
Unit testing best practices with JUnit
 
Best practices unit testing
Best practices unit testing Best practices unit testing
Best practices unit testing
 
Dev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdetDev labs alliance top 20 testng interview questions for sdet
Dev labs alliance top 20 testng interview questions for sdet
 
Test Classes in Salesforce
Test Classes in SalesforceTest Classes in Salesforce
Test Classes in Salesforce
 
How to write effective test cases
How to write effective test casesHow to write effective test cases
How to write effective test cases
 
What is the decision rule all about?
What is the decision rule all about?What is the decision rule all about?
What is the decision rule all about?
 
Test design techniques
Test design techniquesTest design techniques
Test design techniques
 

Similar to 10 Principles of Apex Testing

10 principles of apex testing
10 principles of apex testing10 principles of apex testing
10 principles of apex testingKevin Poorman
 
Generating Test Cases
Generating Test CasesGenerating Test Cases
Generating Test CasesVivekRajawat9
 
Intro to Apex Testing - Dreamforce 2016
Intro to Apex Testing - Dreamforce 2016Intro to Apex Testing - Dreamforce 2016
Intro to Apex Testing - Dreamforce 2016Laura Meerkatz
 
Unit testing (workshop)
Unit testing (workshop)Unit testing (workshop)
Unit testing (workshop)Foyzul Karim
 
QAustral Testing
QAustral   TestingQAustral   Testing
QAustral Testingcusmaim
 
Testcase training
Testcase trainingTestcase training
Testcase trainingmedsherb
 
Testing Software Solutions
Testing Software SolutionsTesting Software Solutions
Testing Software Solutionsgavhays
 
B4 u solution_writing test cases from user stories and acceptance criteria
B4 u solution_writing test cases from user stories and acceptance criteriaB4 u solution_writing test cases from user stories and acceptance criteria
B4 u solution_writing test cases from user stories and acceptance criteriab4usolution .
 
Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)Mary Scotton
 
Best practices for test case creation & maintenance
Best practices for test case creation & maintenanceBest practices for test case creation & maintenance
Best practices for test case creation & maintenance99tests
 
Writing test cases from user stories and acceptance criteria
Writing test cases from user stories and acceptance criteria Writing test cases from user stories and acceptance criteria
Writing test cases from user stories and acceptance criteria An Nguyen
 
Apex 10 commandments df14
Apex 10 commandments df14Apex 10 commandments df14
Apex 10 commandments df14Kevin Poorman
 
Are Your Continuous Tests Too Fragile for Agile?
Are Your Continuous Tests Too Fragile for Agile?Are Your Continuous Tests Too Fragile for Agile?
Are Your Continuous Tests Too Fragile for Agile?Parasoft
 
Advanced Apex Webinar
Advanced Apex WebinarAdvanced Apex Webinar
Advanced Apex Webinarpbattisson
 

Similar to 10 Principles of Apex Testing (20)

10 Principles of Apex Testing
10 Principles of Apex Testing10 Principles of Apex Testing
10 Principles of Apex Testing
 
10 principles of apex testing
10 principles of apex testing10 principles of apex testing
10 principles of apex testing
 
Apex Unit Testing in the Real World
Apex Unit Testing in the Real WorldApex Unit Testing in the Real World
Apex Unit Testing in the Real World
 
Generating Test Cases
Generating Test CasesGenerating Test Cases
Generating Test Cases
 
Intro to Apex Testing - Dreamforce 2016
Intro to Apex Testing - Dreamforce 2016Intro to Apex Testing - Dreamforce 2016
Intro to Apex Testing - Dreamforce 2016
 
Write bulletproof trigger code
Write bulletproof trigger codeWrite bulletproof trigger code
Write bulletproof trigger code
 
Unit testing (workshop)
Unit testing (workshop)Unit testing (workshop)
Unit testing (workshop)
 
QAustral Testing
QAustral   TestingQAustral   Testing
QAustral Testing
 
Test case development
Test case developmentTest case development
Test case development
 
Testcase training
Testcase trainingTestcase training
Testcase training
 
Testing Software Solutions
Testing Software SolutionsTesting Software Solutions
Testing Software Solutions
 
B4 u solution_writing test cases from user stories and acceptance criteria
B4 u solution_writing test cases from user stories and acceptance criteriaB4 u solution_writing test cases from user stories and acceptance criteria
B4 u solution_writing test cases from user stories and acceptance criteria
 
Test cases
Test casesTest cases
Test cases
 
Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)Apex Algorithms: Tips and Tricks (Dreamforce 2014)
Apex Algorithms: Tips and Tricks (Dreamforce 2014)
 
Best practices for test case creation & maintenance
Best practices for test case creation & maintenanceBest practices for test case creation & maintenance
Best practices for test case creation & maintenance
 
Writing test cases from user stories and acceptance criteria
Writing test cases from user stories and acceptance criteria Writing test cases from user stories and acceptance criteria
Writing test cases from user stories and acceptance criteria
 
Apex 10 commandments df14
Apex 10 commandments df14Apex 10 commandments df14
Apex 10 commandments df14
 
Are Your Continuous Tests Too Fragile for Agile?
Are Your Continuous Tests Too Fragile for Agile?Are Your Continuous Tests Too Fragile for Agile?
Are Your Continuous Tests Too Fragile for Agile?
 
Software Testing Presentation
Software Testing PresentationSoftware Testing Presentation
Software Testing Presentation
 
Advanced Apex Webinar
Advanced Apex WebinarAdvanced Apex Webinar
Advanced Apex Webinar
 

More from Kevin Poorman

Mac gyver df14 - final
Mac gyver   df14 - finalMac gyver   df14 - final
Mac gyver df14 - finalKevin Poorman
 
Ionic on visualforce and sf1 df14
Ionic on visualforce and sf1   df14Ionic on visualforce and sf1   df14
Ionic on visualforce and sf1 df14Kevin Poorman
 
Finding a good development partner
Finding a good development partnerFinding a good development partner
Finding a good development partnerKevin Poorman
 
Ci of js and apex using jasmine, phantom js and drone io df14
Ci of js and apex using jasmine, phantom js and drone io   df14Ci of js and apex using jasmine, phantom js and drone io   df14
Ci of js and apex using jasmine, phantom js and drone io df14Kevin Poorman
 
Connecting with the enterprise - The how and why of connecting to Enterprise ...
Connecting with the enterprise - The how and why of connecting to Enterprise ...Connecting with the enterprise - The how and why of connecting to Enterprise ...
Connecting with the enterprise - The how and why of connecting to Enterprise ...Kevin Poorman
 

More from Kevin Poorman (6)

Mac gyver df14 - final
Mac gyver   df14 - finalMac gyver   df14 - final
Mac gyver df14 - final
 
Ionic on visualforce and sf1 df14
Ionic on visualforce and sf1   df14Ionic on visualforce and sf1   df14
Ionic on visualforce and sf1 df14
 
Finding a good development partner
Finding a good development partnerFinding a good development partner
Finding a good development partner
 
Ci of js and apex using jasmine, phantom js and drone io df14
Ci of js and apex using jasmine, phantom js and drone io   df14Ci of js and apex using jasmine, phantom js and drone io   df14
Ci of js and apex using jasmine, phantom js and drone io df14
 
Apex for humans
Apex for humansApex for humans
Apex for humans
 
Connecting with the enterprise - The how and why of connecting to Enterprise ...
Connecting with the enterprise - The how and why of connecting to Enterprise ...Connecting with the enterprise - The how and why of connecting to Enterprise ...
Connecting with the enterprise - The how and why of connecting to Enterprise ...
 

10 Principles of Apex Testing

  • 1. 10 Principles of Apex Testing Kevin Poorman. Sr. Customer Success Architect, Marketing Cloud. Dreamforce 2015
  • 2. Kevin Poorman Kevin Poorman Sr. Customer Success Architect Salesforce Marketing Cloud Twitter: @Codefriar
  • 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; } }
  • 13. Principle #3, Positive Tests private class exampleCode_Tests { @isTest static void test_Add_Postive() { exampleCode drWho = new exampleCode(); Test.startTest(); Integer testValue = drWho.add(5,7); Test.stopTest(); System.assertEquals(12, testValue, ‘Expected 5+7 to equal 12’); }
  • 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; } }
  • 16. Principle #4, Negative Tests private class exampleCode_Tests { @isTest static void test_Divide_Negative() { Boolean didCatchProperException = false; Test.startTest(); Try { exampleCode.divide(1, 0); } catch (exampleCodeException AwesomeException){ didCatchProperException = true; } Test.stopTest(); System.assert(didCatchProperException, ‘Properly caught custom Exception’); } Kittens are crying. Bad Data. No donut.
  • 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