Simplifying Sandbox Refreshes
At OpFocus, our Apex developers often work in newly refreshed Developer sandboxes. Often, though, we’re faced with a challenge: after a Developer sandbox is refreshed/activated, it lacks required reference data. Here are a few scenarios:
- The org has a custom object that lists postal codes and their tax rates. An Opportunity trigger requires a Postal Code record before an Opportunity can be created, so before anyone can do any Opportunity work in the sandbox, somebody has to manually import a list of postal codes.
- A company has a handful of business partners, represented by Account records. Before any testing can happen in the sandbox, somebody has to manually create Account records for these business partners.
- A client with a custom product bundling solution has a custom object that identifies the Products that are associated with each bundle. Before anyone can test Opportunities in the sandbox, the Product Bundling object has to be manually imported into the sandbox.
Certainly, some of this reference data can be stored in custom settings, which is ideal because custom settings are automatically copied to newly refreshed sandboxes. However, we often find reference data is stored in custom objects, which are not automatically copied to new Developer sandboxes. In fact, some of our clients have so much reference data that they avoid refreshing sandboxes whenever possible, with the result that developers may be working in sandboxes that are months or even years out of sync with Production.
Fortunately, Spring ’16 gives us a tool to help solve this problem: the SandboxPostCopy interface.
The SandboxPostCopy interface
The SandboxPostCopy interface allows us to create an Apex class that can be executed automatically after a sandbox is refreshed. Here’s an example:
[code language=”javascript”]
global class OpFocusSandboxPostCopy implements SandboxPostCopy {
global void runApexClass(SandboxContext context) {
// Create the OpFocus Account
Account acctOpFocus = new Account(Name=’OpFocus, Inc.’);
acctOpFocus.BillingStreet = ’78 Blanchard Road, Suite 203′;
acctOpFocus.BillingCity = ‘Burlington’;
acctOpFocus.BillingState = ‘MA’;
acctOpFocus.BillingPostalCode = ‘01803’;
acctOpFocus.BillingCountry = ‘USA’;
insert acctOpFocus;
}
}
[/code]
The runApexClass() method accepts one argument, a SandboxContext. Salesforce doesn’t provide much documentation about the SandboxContext class, but from the related documentation, we can tell that it supports at least the following methods:
- organizationId()
- sandboxName()
With our class defined and deployed to the OpFocus Production instance, whenever we refresh a sandbox, we can tell Salesforce to run our class:
Thus, whenever our sandboxes are refreshed, we can be sure that the reference data we need has been defined before we start to use the sandbox.
What about unit tests, you ask? Unit testing is pretty simple:
[code language=”javascript”]
@isTest
static void testSandboxPostCopyScript() {
OpFocusSandboxPostCopy myPostSandboxCopy = new OpFocusSandboxPostCopy();
Test.testSandboxPostCopyScript(myPostSandboxCopy, null, null, null);
System.assertEquals(1, [select count() from Account]);
}
[/code]
Again, Salesforce doesn’t provide documentation for the Test. testSandboxPostCopyScript(), but it takes the following arguments:
- A reference to a class that implements the SandboxPostCopy interface
- The org Id
- The sandbox Id
- The sandbox name
Although you must provide the first argument, the remaining arguments can be null, provided your SandboxPostCopy class doesn’t need their values.
No more excuses!
Now, with Spring ’16, you finally have a relatively easy way to create the reference data that your sandboxes need, which leaves you with no more excuses for not refreshing your sandboxes more often!