Mastering Application Development With Force - Com - Sample Chapter
Mastering Application Development With Force - Com - Sample Chapter
This book will help you enhance your skillset and develop
complex applications using Force.com. It gets you started
with a quick refresher of Force.com's development
tools and methodologies, and moves on to an in-depth
discussion of triggers, bulkification, DML order of
operations, and trigger frameworks. Next, you will learn
to use batchable and schedulable interfaces to process
massive amounts of information asynchronously. You
will also be introduced to Salesforce Lightning and cover
components in detail.
P U B L I S H I N G
Kevin J. Poorman
Mastering Application
Development with
Force.com
ee
Sa
pl
e
P r o f e s s i o n a l
E x p e r t i s e
D i s t i l l e d
Mastering Application
Development with Force.com
Design and develop state-of-the-art applications using
Force.com's powerful development platform
Kevin J. Poorman
P U B L I S H I N G
Preface
Salesforce.com's platform is one of the most exciting and unique development
platforms for business applications. A lot of Salesforce development can be done
declaratively without writing code, but to truly master the platform, you'll need to be
able to develop not only declaratively, but also with code. Ultimately, you will need
to know when to use which toolsetdeclarative or code.
It is relatively easy, in a world with Salesforce developer forums, Stack Overflow,
and user groups, to find others who have faced the same issues you're facing. As a
developer, it's likely that you can cobble together a solution from posted solutions.
Understanding and tweaking those posts into a solution for your particular problem,
however, requires a greater mastery of the platform.
This book is all about mastering the platform; taking your skills as a developer and
tuning them for the unique features of the Salesforce platform. We'll discuss the
architecture and code and which tool to use for the job. It's going to be awesome. So
let's get started
Preface
Chapter 3, Asynchronous Apex for Fun and Profit, is all about Apex classes that
implement the batchable, scheduleable, and queueable interfaces as well as the
@future method Annotation.
Chapter 4, Lightning Concepts, discusses the four new features of the Salesforce
platform that carry the Lightning moniker. We'll start with Lightning connect and
move on to cover process builder, app builder, and lightning components.
Chapter 5, Writing Efficient and Useful Unit Tests, talks about unit testing, which is the
single most important activity an application developer has to master. However,
writing unit tests is rarely seen as exciting. In this chapter, we'll look at how to write
useful unit tests that help us maintain our application over time.
Chapter 6, Deploying Your Code, takes you to the next stepyou've written and
tested your code, now what? This chapter is a tour of the many ways to deploy
your application metadata from one org to another. Specifically, we'll cover the Ant
migration toolkit, IDE deployment, and Change sets. Additionally, we'll briefly
touch on packaging as a means of deploying metadata.
Chapter 7, Using, Extending, and Creating API Integrations, demonstrates how to use
the sObject and bulk APIs provided by Salesforce as well as how to create your own
custom REST endpoints with Apex. Finally, we'll build out a set of classes to make
calling external REST APIs as painless as possible.
Chapter 8, Team Development with the Salesforce1 Platform, discusses and works through
the pitfalls of team development in general and the unique solutions available to us
on the Salesforce1 Platform.
Chapter 9, My WayA Prescriptive Discussion of Application Development on Salesforce1,
looks at overarching best practices for Architecture and Engineering of applications on
the Force.com platform in depth. Specifically, we discuss the nature of keeping things
simple, testing things well, naming things intuitively, and writing maintainable code.
While the rest of the book has been descriptive of the best practices, this chapter is an
opinionated prescription for developing Salesforce1 applications.
Mocking
[ 73 ]
Proving functionality
The classic reason for unit testing is to prove that the code works as you believe it
does. Given an addition method accepting two integer numbers we can assert the
output of the method when we know the input. Proving the functionality of our
code is the foundation of our testing philosophy because it gives us assurance not
only that our code's architecture, engineering, and logic are correct, but also that we
understand how that code interacts within the greater system.
[ 74 ]
Chapter 5
[ 75 ]
[ 76 ]
Chapter 5
[ 77 ]
[ 78 ]
Chapter 5
While creating your own test data can seem cumbersome and time consuming,
there are a couple of things you can do to help with that. First and foremost, use a
test factory. Test factories provide easy-to-remember methods for generating your
test data. Additionally, they reflect the metadata of your org at runtime, allowing
the test factory to dynamically know which fields are required and how to populate
them intelligently. While there are a number of test factory libraries available, I'm a
fan of Daniel Hoechst's Salesforce Test-Factory, which you can find on GitHub at
https://github.com/dhoechst/Salesforce-Test-Factory. With his test factory
in place, generating your own data for your tests is as simple as a call, like this:
// Calling TestFactory will pre-fill all the fields we need
Account a = (Account)TestFactory.createSObject(new Account());
insert a;
// You can also manually set specific values to be used.
// Any values set in the constructor will override the defaults
Opportunity o = (Opportunity)TestFactory.createSObject(new
Opportunity(AccountId = a.Id));
// You can also specify a specific set of overrides for different
scenarios
Account a = (Account)TestFactory.createSObject(new Account(),
'TestFactory.AccountDefaults');
// Finally, get a bunch of records for testing bulk
Account[] aList = (Account[])TestFactory.createSObjectList(new
Account(), 200);
// You can optionally insert records as created like this:
// Note the final parameter of true.
Account a = (Account) TestFactory.createSObject(new Account(), true);
Contact c = (Contact) TestFactory.createSObject(new Contact(AccountID
= a.Id), true);
[ 79 ]
Even with a test factory in place, it can be cumbersome to have twenty lines of code
in each test simply setting up data. Here's where the second tip comes into play.
While the test factory can provide specific sObjects for us, we often need a collection
of related objects to test with. For instance, we may need an opportunity with
opportunity line items associated with it for our test. Normally, that would require
creating not just the opportunity and opportunity line items but also the account and
any contacts we may need. In these situations, it makes sense to establish one or more
customized test factories that rely on and build on the functionality of the basic test
factory. I like to ensure that everything is easy to find, so I tend to have many sObject
specific test factories. Consider the following instance:
@isTest
Public Without Sharing Class OpportunityTestFactory {
Public Static genOppWithLineItemsAndAccount(Integer
numLineItems, Boolean doInsert){
Account a = (Account)TestFactory.createSObject(New Account(),
doInsert);
Opportunity o = (Opportunity)TestFactory.createSObject(New
Opportunity(accountId=a.id), doInsert);
List<OpportunityLineItem> olis = new
List<OpportunityLineItem>();
for(Integer i = 0; i < numLineItems; i++) {
olis.add((OpportunityLineItem)TestFactory.createSObject(new
OpportunityLineItem(OpportunityId=o.id)));
}
if(doInsert){
insert olis;
}
return o;
}
}
These kinds of customized test factories grow over time but having them means it's
easy to add additional custom methods and easier to write tests using your own
data. Two tips on custom test factories. First, always annotate them as @isTest at
the class level. This ensures that the code cannot be called in a non-test environment
and that it doesn't count against your overall code coverage. Secondly, note how
I've maintained the optional insert flag for the method in the preceding code. If your
test doesn't rely on triggers and other data manipulation language (DML) based
operations, you can often write faster running tests by not inserting or querying.
Oftentimes, however, you will have to insert the data, and maintaining that simple
Boolean flag in your custom factory methods makes it that much easier to do.
[ 80 ]
Chapter 5
[ 81 ]
Positive tests
Positive tests prove expected behavior. That is, they prove that the code functions as
it is intended to when given valid input. Succinctly stated, this subpattern states that
when given proper inputs, expected outputs are received. Care should be taken to
evaluate all valid paths through the code. If your code is structured with conditional
logic statements, ensure that you positively test each and every viable path through
your code. Let's take a look at some class code and it's test to illustrate this:
Public class exampleCode {
Public Integer add(Integer one, Integer two){
return one + two;
}
}
@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');
}
Our simple add() method accepts two integer arguments and returns a single
integer in response. Our test follows our basic test pattern, but the simple nature
of this method means we don't need to create our own test data here; we can just
pass in two integers. You'll note, however, that we're creating the ExampleCode
object before we start the test. Because this is a positive test, we'll provide the add
method with two valid integers, in this case, 5 and 7. Because we're setting the input
parameters, after a couple cups of coffee, we can do the math ourselves and assert
that the value returned is 12. This is an extremely simple but clear example of how
positive tests work.
[ 82 ]
Chapter 5
Negative tests
Negative tests are less intuitive and require some additional setup. In return,
negative tests prove that our code safely handles errors and exceptions. Because
we're intentionally providing inputs to the code that will cause an exception to
occur, we need to find a way to safely capture the exception without failing the test.
Here, the subpattern works by setting a Boolean variable, say didPass to false in
the setup of the test, and executing our to-be-tested code inside a Try/catch block.
When our inputs cause the test to throw the exception, our test captures that in the
catch block. If the type of exception and messaging we catch matches what we
expect, we can set didPass to true. Outside of the Try/catch block and after the
stopTest() call, we can assert that our didPass variable is true. That's a lot to take
in, so let's look at the following code example:
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;
}
}
private class exampleCode_Tests {
@isTest static void test_Divide_Negative() {
Boolean didCatchProperException = false;
Test.startTest();
Try {
exampleCode.divide(1, 0);
} catch (exampleCode.exampleCodeException AwesomeException){
didCatchProperException = true;
}
Test.stopTest();
System.assert(didCatchProperException,
'Properly caught custom Exception');
}
[ 83 ]
We've added a division method to our ExampleCode class, and astute readers will
notice the addition of a new custom exception class, exampleCodeException. In
our test, we follow the same pattern as earlier of starting the test and executing
our code. However, in this situation, we wrap the code execution in a Try/catch
block. We're calling our divide method with an intentionally bad second integer,
0. Our divide method is on the lookout, however, for the second parameter being
0. Instead of letting the system throw a divide by zero error, our code throws an
ExampleCodeException method. It's this exampleCodeException method that
we're trapping in our test's catch block. This allows us to be certain that not just any
exception is caught here. If, for instance, a divide by zero exception somehow still
occurred, that exception would not be caught and the test would fail. In the end, we
set didCatchProperException, our previously defined test Boolean to true so that
we can assert that we did indeed catch the proper kind of exception.
Negative tests are considerably more powerful in the long run, helping ensure
modifications still properly handle known exception situations properly. This
is especially helpful when modifying the implementation of class methods.
Additionally, because they're testing how the code reacts to invalid data, they
are the de facto way of testing situations, such as invalid user entered data, fields
that legitimately have apostrophes in them, and responses to third-party external
web services that your code uses. Does your code properly handle the last name
O'Connel? How about a fellow developer accidently linking a task by WhatId
instead of WhoId? And how does your exchange rate API code handle the sudden
lack of Internet connection due to severe weather? Negative tests ensure that you
can answer these questions. More importantly, negative tests prove that you've
defensively developed against at least those failure scenarios that were identified.
Permissions-based tests
Permissions-based testing ensures that your sharing and security model works as
you expect it to. Sharing rules and security options are some of the most complicated
aspects of Salesforce1 platform development. These are, therefore, some of the most
important tests you can write. But wait you say! Isn't writing tests of the security
and permissions model just a form of testing native platform functionality? Kind
of. Unless otherwise specified, Apex code runs in a system context. In essence, this
means that Apex code normally ignores the users' permissions and sharing rules.
These tests help us ensure that the permissions and sharing rules are honored by our
Apex code.
[ 84 ]
Chapter 5
Effectively, permission tests follow the same general pattern as other tests with one
small twist. When we go to execute the code, we'll execute that code with a different
user. We're not just going to pick a user at random either. The user we run the test
with becomes a crucial data point that we create. To actually run the test with our
newly created user, we call the Salesforce1 platform's System.runAs() method. The
RunAs method accepts a single user parameter and a block. Anything in that block of
code is executed as with the profile, role, and permission sets of the user specified.
Let's look at a basic use case for runAs():
private class exampleCode_Tests {
@isTest static void test_getBankAccount_AsUserWithTimeLord() {
User u = UserTestFactory.getUserWithProfile('TimeLord');
System.runAs(u){
Test.startTest();
// This is executed as our user with the Timelord
// profile
Test.stopTest();
}
// Assertions
}
In this test method, we create a user with a call to our UserTestFactory. This user
is set up with a given profile, in this case, the profile TimeLord. This sets us up to
test our code as TimeLord. Now, when we execute our code, we can ensure that this
profile's permissions are honored.
With this subpattern, we can test not only profiles, but also sharing rules and
permission sets. Perhaps more importantly, we can test them both positively and
negatively! Let's look at some examples. First, let's update our ExampleCode class
with a method we want to permission test:
Public class exampleCode {
Public class exampleCodeException{}
Public Integer getBankAccount(Account a){
// SuperSekr3tBankAccountNum__c is an encrypted field
a = [SELECT superSekr3tBankAccountNum__c
FROM Account
WHERE ID :a.id];
If(String.ValueOf(a.superSekr3tBankAccountNum__c).contains('*')) {
Throw new exampleCodeException('Nope!');
}
return a.SuperSekr3tBankAccountNum__c;
}
}
[ 85 ]
In this test, we expect a user with the TimeLord profile to be able to access the
encrypted bank account number field. On the other hand, we want to ensure that
other profiles do not have access. With this in mind, we can write a negative test that
looks like this:
@isTest
private class exampleCode_Tests {
@isTest static void test_getBankAccount_UberForNope() {
exampleCode Dalek = new exampleCode();
User u = UserTestFactory.getUserWithProfile('Dalek');
Account a = (Account)TestFactory.createSObject(new Account());
Boolean didCatchException = false;
Integer result;
System.runAs(u){
Test.startTest();
Try {
result = Dalek.getBankAccount(a);
} catch(exampleCode.ExampleCodeException e){
if(e.getMessage().containsIgnoreCase('nope')){
didCatchException = true;
}
Test.stopTest();
}
System.assert(didCatchException, 'Expected Daleks to be blocked');
}
[ 86 ]
Chapter 5
Our getBankAccount() method queries for an encrypted field; if the user doesn't
have permission to view it, it will return a masked value. If we detect that masked
value, we throw an exception. Like our positive test, this test still requires a custom
user with a given profile, but in this case we expect an exception.
Importantly, we're not limited to testing users with different profiles. We can,
and should create tests for Apex-based sharing rules, roles, and permission sets.
Permission sets are an incredibly powerful and fine-grained tool for extending
permissions on a per-user basis, beyond what a user has from their profile. With
permission sets, we can, for instance, establish a singular profile for the entire
support staff and grant additional privileges to support managers. If you're
not already a fan of permission sets, check them out, you soon will be! Testing
permission sets requires just a few extra lines of code to test. This is a prime example,
however, of where a custom test factory becomes an invaluable tool. Let's look at
some code as another example of testing permission sets:
@isTest
private class exampleCode_Tests {
@isTest static void test_getBankAccount_W_PermSet() {
exampleCode ClaraOswald = new exampleCode();
User u = UserTestFactory.getUserWithProfileAndPermSets('Standard
User', new List<String>{'companion'});
Account a = (Account)TestFactory.createSObject(new Account());
Boolean result;
System.runAs(u){
Test.startTest();
result = ClaraOswald.getBankAccount(a);
Test.stopTest();
}
System.assertNotEquals(result, null,
'Expected ClaraOswald who has Companion Permissions to have access
to the bank account');
}
As you can see from the code, our test is nearly identical to our positive
profile permission test. In fact, the only difference in this test is the custom
userTestFactory method we called. Because positive permission tests are so
similar, you can often group them together in a data structure. This allows you to
write a metatest that iterates over your data structure to test various permissions.
This greatly simplifies your testing, allowing you to add an element to your data
structure, instead of adding entirely new tests whenever a new permission set or
profile is created. Here's how one such meta-test works:
@isTest
Public Class accountPermTests {
[ 87 ]
Chapter 5
exampleCode instance = new ExampleCode();
User u;
Boolean didCatchException;
Integer result;
Account a = (Account)TestFactory.createSObject(new Account());
if(p.isPermSetTest && p.isProfileTest
&& p.profileName != '' && p.permSetName != ''){
u = UserTestFactory.getUserWithProfileAndPermSets
(p.profileName, new List<String>{p.permSetName});
} else if (p.isProfileTest && p.profileName != ''){
u = UserTestFactory.getUserWithProfile(p.profileName);
}
Test.startTest();
System.runAs(u){
if(p.isPositiveTest) {
result = instance.getBankAccount(a);
} else {
try {
result = instance.getBankAccount(a);
} catch(Exception e) {
if(e.getTypeName() == p.exceptionTypeName &&
e.getMessage().containsIgnoreCase(p.exceptionMessage)){
didCatchException = true;
}
}
}
}
Test.stopTest();
if(p.isPositiveTest){
System.AssertEquals(p.AssertEquals, Result,
p.friendlyMessage);
} else {
System.assert(didCatchException, p.friendlyMessage);
}
}
}
A test setup like this has some upfront costs, namely writing the inner class
data structure and thinking through what commonalities exist across your
permissions-based tests. In the end though, it's much easier to maintain such a suite
of tests, as you can add or modify the .add calls in setPermissionTestData much
faster than writing a net-new test.
[ 89 ]
Assertions
If you take nothing else away from this chapter, let it be thistests without
assertions are not tests, but liabilities. Without the assert calls, you cannot check the
outcome of your executed code block to see if it functioned properly. That's why
I refer to test methods without assert calls as liabilities. Inevitably, when the code
fails for whatever reason, you're accountable for the consequences. At 3am. On a
Saturday. It's even worse, if your code is mission critical and fails at the quarter's
end! Practice safe testing, use asserts.
The Salesforce1 platform provides us with three basic assertion methods. The first
method, System.Assert(expression), evaluates the expression within for a
Boolean true or false. Thus, you can use it like this:
System.assert(1 = 1)
System.assert(BooleanVariable)
System.assert(p != np)
The other two built-in assertion methods are really shorthand, convenience methods
built on top of System.assert. They are System.assertEquals(expected,
actual) and System.assertNotEquals(expected, actual). These often read
easier than a simple System.assert() call. Here are a few examples:
System.assertEquals(1,1)
System.assertNotEquals(false, BooleanVariable)
System.assertNotEquals(P, NP)
It's important to remember that each of these built-in assert methods can accept an
optional final parameter that I call the friendly message. You should always use the
friendly message option, as it will help you debug which assertions and which tests
are failing. You will only ever see the friendly message when a test fails. To use the
friendly message, simply add a string parameter to the method call; for instance:
System.assertNotEquals(P, NP, 'Oh noes! P = np means all cryptography
is flawed!')
[ 90 ]
Chapter 5
Here, we're both compiling standard assertions that now must all pass as well as a
custom comparison. If any of the three standard assertions called here fail, or if the
custom full name comparison fails, an exception is raised and the assertion fails.
[ 91 ]
We talked about what asserts are and how to use them, so let's take a second to
consider when to use them. All tests should have at least one assertion after the call
to stopTest(). Better tests will have multiple assertions identifying, for instance,
that the object returned not only had the proper record type, but that it was properly
modified by the executed code. Best yet, are the test methods that include two
assertion blocks in addition to the standard after stopTest() assertions, the best
tests will include a block asserting that your test data was created properly. This
is especially important as you try to identify falsely failing tests. If the code works
when manually executed via anonymous Apex or the UI, but fails in the tests, you
may have a problem with your test data. Asserting that X number of contacts were
created helps you identify the problem.
Mocking
Mocking is the practice of substituting the standard implementation of classes and
methods with stub methods that return a fixed value. Other development stacks have
rich and robust mocking capabilities built in. Salesforce1, on the other hand, is slowly
expanding into the mocking world. In fact, there's only one built-in mock interface
for you to stub. Despite the lack of a robust mocking library built into the platform,
the capabilities of the existing mock interface make unit testing HTTP callouts a
breeze. Additionally, there are other, third-party, mocking libraries that work with
the Salesforce1 platform. Libraries, such as FFLib_ApexMocks, found at https://
github.com/financialforcedev/fflib-apex-mocks, allow you to stub custom
objects and methods so long as you have written your class to implant an interface.
Let's take a deeper look at the HTTPCalloutMock interface. Like most interface
implanting classes, there are required methods for you to implement. In the case of
HTTPCalloutMock, we must implement the response() method. This method must
accept a HTTPRequest object as it's parameter and must return a HTTPResponse
object. How we stub that out is up to us. To use our mock object in a test, we simply
call: test.setMock(MockObj). After that mock is set, the next callout made will
automatically return our stubbed HTTPResponse object. Rather than cluttering up my
org with dozens of classes implementing the HTTPCalloutMock interface, I like to
code a factory class that constructs the mock for me:
@isTest
public with sharing class CalloutMockFactory implements
HttpCalloutMock {
Protected Integer
code;
Protected String
status;
Protected String
bodyAsString;
Protected Blob
bodyAsBlob;
Protected Map<String, String> responseHeaders;
[ 92 ]
Chapter 5
public CalloutMockFactory(Integer code, String status, String
body, Map<String, String> responseHeaders) {
this.code = code;
this.status = status;
this.bodyAsString = body;
this.bodyAsBlob = null;
this.responseHeaders = responseHeaders;
}
public HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(this.code);
res.setStatus(this.status);
res.setBody(this.bodyAsString);
return res;
}
}
With this factory in place I can easily test callouts by using the factory to return a
response as it's needed. For example:
Test.setMock(HttpCalloutMock.class, new CalloutMockFactory(400,
'Invalid Request', ps_GuidResp_Tests.json_error, null));
If you don't need to, don't insert and query data to and from the database.
Starting with winter '13, you can set a value for the Id field, so long as you
don't try to insert it. This allows you to create an object, set its ID, and create
other objects that reference it. The slowest part of any web-based application
is historically the database, and Salesforce1 is no exception. If you can cut
down your SOQL and DML, your tests will run faster.
Find a general mocking library and use it. The more complex your org
becomes, the longer it will take to run your tests. If you're mocking out
objects and methods whose return values are crucial to the code being tested
but not the tested code itself, you not only add stability to your tests, you
know what the mock object or stubbed method will return each time! But,
you also speed up your code by bypassing all of the code those objects would
be running! Additionally, you should mock every single HTTP callout!
[ 94 ]
Chapter 5
Always test your code with bulk data, but change the volume of data per
environment. Proving that your code is bulk safe doesn't have to happen in
production. Create a custom setting for TestOptions and create a numeric
field titled EnvironmentBulkSize. Reference that custom setting in your test
cases as you can see in the following code. Remember to set your sandbox's
EnvironmentBulkSize option to 200 but set your production value to
something like 5. The less the work, the faster the tests:
@isTest
private class ExampleCodeTests {
TestOptions__c options;
@testSetup static void setup() {
options = TestOptions__c.getInstance('default');
}
@isTest static void someTest(){
Account[] accounts =
(Account[])TestFactory.createSObjectList(new Account(),
options.EnvironmentBulkSize);
}
Summary
Throughout this chapter, we covered in great detail the why, when, and how of
writing unit tests. There's a lot of information and nuance in this chapter. However,
I hope you can walk away with not only a grasp of the fundamental pattern for unit
testing, but also its three subpatterns of positive, negative, and permissions-based
testing. Remember as well that a test method without at least one assert method is
a liability. Furthermore, it's likely not going to be useful in the long run. Use asserts
liberally. Assert not only the expected response of the unit of code, but also the data
you created. Once your tests are written and passing, you'll be able to deploy your
code to production. We'll talk about deployment strategies in our next chapter.
[ 95 ]
www.PacktPub.com
Stay Connected: