30
Introduction to Apex Testing Salesforce World Tour Boston

Apex Testing and Best Practices

Embed Size (px)

Citation preview

Page 1: Apex Testing and Best Practices

Introduction to Apex TestingSalesforce World Tour Boston

Page 2: Apex Testing and Best Practices

Jitendra ZaaForce.com MVP

@JitendraZaa

http://JitendraZaa.com

Page 3: Apex Testing and Best Practices
Page 4: Apex Testing and Best Practices

• Why do we need unit test

• Getting started with Apex test classes

• Testing Visualforce Controller

• Web services Testing

• Testing Apex Callouts

• Best Practices

• Q&A

Agenda

Page 5: Apex Testing and Best Practices

• Development of robust, error-free code

• As Salesforce is multitenant platform, make sure none of governor limits are hitting

• To deploy code on production, unit tests are required

• Minimum 75% of test coverage is required

• Code coverage = number of unique apex lines executed / total number of lines in trigger , classes

• These excludes comments, test methods

• Unit tests are critical part of Salesforce development as it ensures success

• Provides automated regression testing framework to make sure bug free future development

Why unit test

Page 6: Apex Testing and Best Practices

• Apex code that tests other Apex code

• Annotate class with @isTest

• Class methods (static)

• Defined with testMethod keyword

• Classes defined with @isTest annotation does not count against organization limit of 2MB of Apex code.

How to write unit test

Page 7: Apex Testing and Best Practices

@isTest

public class myClass {

static testMethod void myTest() {

// Add test method logic using System.assert(), System.assertEquals()

// and System.assertNotEquals() here.

}

}

Sample Code

Page 8: Apex Testing and Best Practices

• Used to test governor limits

• To make sure all Asynchronous code executes when stoptest() method is called

• When startTest() method is called, fresh governor limits are applied until stopTest() is executed

• startTest() and stopTest() can be called only once in testMethod.

Test.startTest() and Test.stopTest()

Page 9: Apex Testing and Best Practices

trigger OverwriteTestAccountDescriptions on Account (before insert) {

for(Account a: Trigger.new){

if (a.Name.toLowerCase().contains('test')){

a.Description =

'This Account is probably left over from testing. It should probably

be deleted.';

}

}

}

Trigger to be tested

Sample Code

Page 10: Apex Testing and Best Practices

static testMethod void verifyAccountDescriptionsWhereOverwritten(){

// Perform our data preparation.

List<Account> accounts = new List<Account>{};

for(Integer i = 0; i < 200; i++){

Account a = new Account(Name = 'Test Account ' + i);

accounts.add(a);

}

// Start the test, this changes governor limit context to

// that of trigger rather than test.

test.startTest();

Test method to test Trigger

Sample Code

Page 11: Apex Testing and Best Practices

// Insert the Account records that cause the trigger to execute.

insert accounts;

// Stop the test, this changes limit context back to test from trigger.

test.stopTest();

// Query the database for the newly inserted records.

List<Account> insertedAccounts = [SELECT Name, Description

FROM Account

WHERE Id IN&nbsp;:accounts];

// Assert that the Description fields contains the proper value now.

for(Account a&nbsp;: insertedAccounts){

System.assertEquals(

'This Account is probably left over from testing. It should probably

be deleted.',

a.Description);

}

}

Sample Code (cont.)

Page 12: Apex Testing and Best Practices

• By default Apex code runs in system mode

• Permission and record sharing are not taken into consideration

• System.runAs() method helps to change test context to either existing or new user

• System.runAs() can only be used in testMethods

System.runAs()

Page 13: Apex Testing and Best Practices

public class TestRunAs {

public static testMethod void testRunAs() {

// This code runs as the system user

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]');

System.runAs(u) {

// The following code runs as user 'u'

System.debug('Current User: ' + UserInfo.getUserName());

System.debug('Current Profile: ' + UserInfo.getProfileId()); }

// Run some code that checks record sharing

}

}

Sample Code

Page 14: Apex Testing and Best Practices

• Use TestVisible annotation to allow Test methods to access private or protected members of another class

• These members can be methods , member variables or inner classes

@TestVisible

Page 15: Apex Testing and Best Practices

public class TestVisibleExample {

// Private member variable

@TestVisible private static Integer recordNumber = 1;

// Private method

@TestVisible private static void updateRecord(String name) {

// Do something

}

}

Actual code to Test

Sample Code

Page 16: Apex Testing and Best Practices

@isTest

private class TestVisibleExampleTest {

@isTest static void test1() {

// Access private variable annotated with TestVisible

Integer i = TestVisibleExample.recordNumber;

System.assertEquals(1, i);

// Access private method annotated with TestVisible

TestVisibleExample.updateRecord('RecordName');

// Perform some verification

}

}

Test Method

Sample Code

Page 17: Apex Testing and Best Practices

• Like all Apex classes and Triggers, Visualforce controllers also requires Test methods

• Test methods can automate user interaction by setting query parameter or navigating to different pages

Test Visualforce Controller

Page 18: Apex Testing and Best Practices

public static testMethod void testMyController() {

//Use the PageReference Apex class to instantiate a page

PageReference pageRef = Page.success;

//In this case, the Visualforce page named 'success' is the starting

point of this test method.

Test.setCurrentPage(pageRef);

//Instantiate and construct the controller class.

Thecontroller controller = new Thecontroller();

//Example of calling an Action method. Same as calling any other Apex

method.

String nextPage = controller.save().getUrl();

//Check that the save() method returns the proper URL.

System.assertEquals('/apex/failure?error=noParam', nextPage);

}

Sample Code

Page 19: Apex Testing and Best Practices

• Generated code from WSDL is saved as Apex class and therefore needs to be tested

• By default test methods does not support Web service call outs

• Apex provides the built-in WebServiceMock interface and the Test.setMock method that you canuse to receive fake responses in a test method.

• Instruct the Apex runtime to generate a fake response whenever WebServiceCallout.invoke iscalled.

Testing Web Services

Page 20: Apex Testing and Best Practices

@isTest

global class WebServiceMockImpl implements WebServiceMock {

global void doInvoke(

Object stub,

Object request,

Map<String, Object> response,

String endpoint,

String soapAction,

String requestName,

String responseNS,

String responseName,

String responseType) {

docSample.EchoStringResponse_element respElement =

new docSample.EchoStringResponse_element();

respElement.EchoStringResult = 'Mock response';

response.put('response_x', respElement);

}

}

Sample Code

Page 21: Apex Testing and Best Practices

public class WebSvcCallout {

public static String callEchoString(String input) {

docSample.DocSamplePort sample = new docSample.DocSamplePort();

sample.endpoint_x = 'http://api.salesforce.com/foo/bar';

// This invokes the EchoString method in the generated class

String echo = sample.EchoString(input);

return echo;

}

}

Actual code that calls Web service

Sample Code

Page 22: Apex Testing and Best Practices

@isTest

private class WebSvcCalloutTest {

@isTest static void testEchoString() {

// This causes a fake response to be generated

Test.setMock(WebServiceMock.class, new WebServiceMockImpl());

// Call the method that invokes a callout

String output = WebSvcCallout.callEchoString('Hello World!');

// Verify that a fake result is returned

System.assertEquals('Mock response', output);

}

}

Test class to test Web service response

Sample Code

Page 23: Apex Testing and Best Practices

• Apex has ability to call external Web services like Amazon, Facebook or any other Web service.

• Salesforce does not has any control over response of external Web services.

• Apex callouts can be tested either using HttpCalloutMock interface or using Static Resources.

• HttpCalloutMock Is used to generate fake HttpResponse for testing purpose.

Testing Apex Callouts

Page 24: Apex Testing and Best Practices

@isTest

global class MockHttpResponseGenerator implements HttpCalloutMock {

// Implement this interface method

global HTTPResponse respond(HTTPRequest req) {

// Create a fake response

HttpResponse res = new HttpResponse();

res.setHeader('Content-Type', 'application/json');

res.setBody('{"foo":"bar"}');

res.setStatusCode(200);

return res;

}

}

Implementing HttpCalloutMock interface

Sample Code

Page 25: Apex Testing and Best Practices

public class CalloutClass {

public static HttpResponse getInfoFromExternalService() {

HttpRequest req = new HttpRequest();

req.setEndpoint('http://api.salesforce.com/foo/bar');

req.setMethod('GET');

Http h = new Http();

HttpResponse res = h.send(req);

return res;

}

}

Actual code to be tested

Sample Code

Page 26: Apex Testing and Best Practices

@isTest

private class CalloutClassTest {

@isTest static void testCallout() {

// Set mock callout class

Test.setMock(HttpCalloutMock.class, new MockHttpResponseGenerator());

// This causes a fake response to be sent

// from the class that implements HttpCalloutMock.

HttpResponse res = CalloutClass.getInfoFromExternalService();

// Verify response received contains fake values

String contentType = res.getHeader('Content-Type');

System.assert(contentType == 'application/json');

String actualValue = res.getBody();

String expectedValue = '{"foo":"bar"}';

System.assertEquals(actualValue, expectedValue);

System.assertEquals(200, res.getStatusCode());

}

}

Complete Test methods

Sample Code

Page 27: Apex Testing and Best Practices

• Test methods take no arguments, commit no data to the database, and cannot send any emails.

• Strive for 100% code coverage. Do not focus on the 75% requirement.

• Write portable test methods and do not hardcode any Id or do not rely on some existing data.

• If possible don’t use seeAllData=true annotation

• Use System.assert methods to prove that code behaves properly.

• In the case of conditional logic (including ternary operators), execute each branch of code logic.

• Use the runAs method to test your application in different user contexts.

• Exercise bulk trigger functionality—use at least 20 records in your tests.

Best Practices

Page 29: Apex Testing and Best Practices

Go though Apex Testing module of Trailhead and earn your badge

Trailhead

Page 30: Apex Testing and Best Practices

Thank you