46
10 Principles of Apex Testing May 21 st , 2015

10 Principles of Apex Testing

Embed Size (px)

Citation preview

10 Principles of Apex TestingMay 21st, 2015

#forcewebinar

Speakers

Kevin Poorman

Principle Architect, EDL

Consulting

@Codefriar

#forcewebinar

Safe HarborSafe 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.

#forcewebinar

Go Social!

Salesforce Developers

+Salesforce Developers

Salesforce Developers

Salesforce Developers The video will be posted to

YouTube & the webinar recap

page (same URL as registration).

This webinar is being recorded!

@salesforcedevs / #forcewebinar

So make sure to

leave comments

on YouTube

#forcewebinar

▪ Don’t wait until the end to ask your question!

– Technical support will answer questions starting now.

▪ Respect Q&A etiquette

– Please don’t repeat questions. The support team is working

their way down the queue.

▪ Stick around for live Q&A at the end

– Speakers will tackle more questions at the end, time-

allowing. If no one asks questions, Kevin will.

▪ Head to Developer Forums

– More questions? Visit developer.salesforce.com/forums

Have Questions?

#forcewebinar

Agenda

1. Why we do Testing

2. 10 principles of Apex Testing

3. Questions and Answers

#forcewebinar

Why test? (when you have users)

#forcewebinar

Why we test?

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

#forcewebinar

Principle #1, Use Asserts

A test without Assert methods isn’t a test, it’s code

execution

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

#forcewebinar

Principle #1, Use Asserts

You can write your own assert methods!

@isTest

Public class customAssertions{

public class customAssertionException extends Exception{}

Public Boolean DuplicateAccount(Account a, Account b){

// … compare accounts

if(duplicate != true) {

Throw new customAssertionException(‘whoa, it’s not the

same’);

}

return true;

}

}

#forcewebinar

Principle #2, use startTest and stopTest

Test.startTest() and Test.stopTest() help facilitate

testing.

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.

#forcewebinar

Principle #2, use startTest and stopTest

General pattern is to

– Create your test data

– Start the test

– Use that test data within your tested method

– End the Test

– Assert your code works as expected

#forcewebinar

Principle #3, Positive Tests

Write ‘Positive Tests’

Positive tests test the expected behavior

Lets talk about multiple expected behaviors

Not just happy path testing

#forcewebinar

Principle #3, Positive Tests

Public class exampleCode {

Public Integer add(Integer one, Integer two){

return one + two;

}

}

#forcewebinar

Principle #3, Positive Tests

@isTest

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’);

}

#forcewebinar

Principle #4, Negative Tests

Negative tests prove that your code properly handles

exceptions and errors.

The general pattern is to call your method within a

try/catch block in the test. Since you expect an

exception you’ll catch it in the catch block

Less intuitive but more powerful!

#forcewebinar

Principle #4, Negative Tests

Public class exampleCode {

Public class exampleCodeException{}

Public Integer division(Integer one, Integer two){

if(two == 0) {

Throw new exampleCodeException(‘Dividing by zero makes kittens cry’);

}

return one / two;

}

}

#forcewebinar

Principle #4, Negative Tests@isTest

private class exampleCode_Tests {

@isTest static void test_Divide_Negative() {

exampleCode drWho = new exampleCode();

Boolean didCatchProperException = false;

Test.startTest();

Try {

drWho.divide(1, 0);

} catch (exampleCodeException AwesomeException){

didCatchProperException = true;

}

Test.stopTest();

System.assert(didCatchProperException,

‘Properly caught custom Exception’);

}

#forcewebinar

Principle #5, User Tests

User based tests prove your security model

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.

#forcewebinar

Principle #5, User Tests

Public class exampleCode {

Public class exampleCodeException{}

Public Integer getBankAccount(Account a){

return a.SuperSecretBankAccountNum__c;

}

}

#forcewebinar

Principle #5, User Tests Positive@isTest

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 this user to have access to bank #’);

}

#forcewebinar

Principle #5, User Tests - Negative@isTest

private class exampleCode_Tests {

@isTest static void test_getBankAccount_UberForNope() {

exampleCode drWho = new exampleCode();

User u = AwesomeTestLib.getUserWithProfile(‘SithProfile’);

Account a = (Account)TestFactory.createSObject(new Account());

Integer result;

System.runAs(u){

Test.startTest();

result = drWho.getBankAccount(a);

Test.stopTest();

}

System.assertEquals(result, null,

‘Expected Sith lords to be blocked’);

}

#forcewebinar

Principle #5, User Tests – w/ Permission set@isTest

private class exampleCode_Tests {

@isTest static void test_getBankAccount_W_PermSet() {

exampleCode drWho = new exampleCode();

User u = AwesomeTestLib.getUserWithProfile(‘Standard User’);

UtilityClass.AssignUserToPermissionSet(u, ‘canReadBankAccount’);

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 user with canReadBankAccount to read BankAccount#’);

}

#forcewebinar

Principle #6, Use your own data

Always build your own test data.

Unless you have to, never use

@isTest(seeAllData=true)

Rember you can dynamically write assertions:

– AssertEquals(AccountICreated.foo__c, ResultAccount.foo__c,

‘the two accounts should be the same!’);

#forcewebinar

Principle #6, Use your own data

General pattern is to

– Create your test data

– Use that test data within your tested method

– Assert your code works as expected

#forcewebinar

Principle #6, Use your own data

Tools to make this faster:

– TestFactory, An open source test data factory from Daniel

Hoechst. Found at http://bit.ly/1c5exnV

– 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);

#forcewebinar

Principle #7, Use a domain specific test helper lib

Test Helpers facilitate faster test writing

Additionally make tests easier to read, by abstracting

out superfluous code.

Mark your test helper as @isTest to ensure it’s never

called by live code, and to ensure it’s not counted

against you in code coverage

#forcewebinar

Principle #7, Use a domain specific test helper lib

Candidates for Test Helper classes include:

– Creating a user object with a given permission set assigned

– Generating complex object trees:

• generateAccountWOppAndProducts()

– Anything you do more than once in a test suite

– Make them public static methods and use a naming convention

• generate* - generates an object

• get* - retrieves an object via SOQL

#forcewebinar

Principle #8, Mocking

Integration v. Unit tests

– Integration tests test code in an execution context – Ie 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 ‘mocking’ objects.

– You get to setup the mock, and it’s responses.

#forcewebinar

Principle #8, Mocking

Unit TestCalls a service

Returns ???

Traditional integration testing

#forcewebinar

Principle #8, Mocking

Unit testing with mocks

MockObj

Unit Test

Returns known result

Returns known result

#forcewebinar

Principle #8, Mocking

Use mocks to insert objects whenever that object is not

critical to the test.

– For instance, if you’re testing a method that populates an

Account with the results of a webservice, mock out the web

services’ result.

– This allows you to test just what that method does to the

account object, not it’s ability to talk to a web service

#forcewebinar

Principle #8, Mocking

Not just for webservices – Mock all services!

– Mock anything you have in your service layer

– Use interfaces and write the unit test with a mock

implementation

#forcewebinar

Principle #8, Mocking@isTest

Public class exampleCode_Tests{

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’);

}

}

#forcewebinar

Principle #8, Mocking Part 2

Mocking HTTP Responses

Allows you to test HTTP Callouts without making the

Callout.

Interface requires you to return an HTTP Response

object.

#forcewebinar

Principle #8, Mocking Part 2 – Use a factory

@isTest

public with sharing class HTTPMockCalloutFactory implements HttpCalloutMock {

HTTPMockCalloutFactory (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;

}

}

#forcewebinar

Principle #8, Mocking Part 2 – Use a factory

You can even extend your factory to return different

response based on a static class variable. Useful for

testing multiple callout scenarios.

– Write an additional constructor that accepts a list of json strings

that de-serialize into responses

#forcewebinar

Principle #9, Write for testing

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, and integration tests for customized logic.

#forcewebinar

Principle #9, Write for testing: guidelines

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!

#forcewebinar

Principle #10, Use Continuous Integration!

Continuous Integration is the idea that every code-

change committed to your source repository triggers a

full run of the tests.

A number of tools are available for CI on the

Salesforce1 platform.

Travis.ci, Drone.io, Jenkins and Codeship are some

examples

#forcewebinar

Principle #10, Use Continuous Integration!

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.

#forcewebinar

Useful tips

Don’t insert or query unless you have to. You can often

test with objects in memory!

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

#forcewebinar

Survey

Your feedback is crucial to the success

of our webinar programs. Thank you!

http://bit.ly/1FCyGiR

#forcewebinar

Q & A

Survey Link: http://bit.ly/1FCyGiR

Kevin Poorman

Principle Architect, EDL

Consulting

@Codefriar

Thank You