Monthly Archives: August 2017

A Pattern for Apex Test Code So You Can Forget About Required Fields

Published by:

I recently had the opportunity to start writing some code in a brand new org, which got me thinking about the best way to do test object creation. You know, that annoying problem where you need to generate an Account in a test class about 77 times. Here’s a pattern I came up with.

This method involves some overhead but it has the advantages of (1) allowing you to set defaults for certain objects so you don’t have to worry about them – for some objects create them with all defaults with just one line, and (2) a generic, repeatable pattern.

It’s based on an abstract class that implements the base operations using some generic sObject magic outlined here.

A lot of code in this post, bear with me.

public abstract class TestObjectFactory{

    public abstract Schema.SObjectType getSObjectType();
    public abstract String getSObjectAPIName();
    List<Test_Class_Default__mdt> defaults = new List<Test_Class_Default__mdt>();
private Map<SobjectField,Object> values = new Map<SobjectField,Object>();

    public void setFieldValue(sObjectField field,Object value)

    public void clearFieldValues()
    	values = new Map<SobjectField,Object>();

   public SObject createObject( Map<SObjectField, Object> valuesByField, Boolean doInsert) {
        // Initialize the object to return
        SObject record = this.getSObjectType().newSObject(null, true);

        // Fill defaults 
        record = fillDefaults(record,false);

        // Populate the record with values passed to the method
        if (valuesByField != null)
	        for (SObjectField eachField : valuesByField.keySet()) {
	            record.put(eachField, valuesByField.get(eachField));
        if (doInsert)
        	insert record;
        // Return the record
        return record;

    // Overload version that just uses defaults
	public SObject createObject( Boolean doInsert) {
        // Initialize the object to return
        SObject record = this.getSObjectType().newSObject(null, true);
        // Fill defaults 
        record = fillDefaults(record, false);
        if (doInsert)
        	insert record;
        return record;
    public void updateObject(sObject record, Map<SObjectField, Object> valuesByField)
    	if (record.Id == null)
    		throw new MyException('Test Object Factory: Attempt to Update a Record with a Null ID');
       	for (SObjectField eachField : valuesByField.keySet()) {
            	record.put(eachField, valuesByField.get(eachField));
        update record;


    public SObject upsertObject(sObject record, Map<SObjectField, Object> valuesByField)
    	for (SObjectField eachField : valuesByField.keySet()) {
            record.put(eachField, valuesByField.get(eachField));
        upsert record;
        return record;

    public void deleteObject(sObject record)
    	String recordId = record.Id;

For each object you will use, you need to create a class that implements the abstract factory class.

public class TestObjectFactoryAccount extends TestObjectFactory{

    public override Schema.SObjectType getSObjectType(){
        return Account.SObjectType;
    public override String getSObjectAPIName(){
        return 'Account';
    public Account createRecord(Map<SObjectField, Object> valuesByField, Boolean doInsert){
        return (Account) createObject(valuesByField, doInsert);
    public Account createRecord(Boolean doInsert){
        return (Account) createObject( doInsert);
    public void updateRecord(sObject record, Map<SObjectField, Object> valuesByField){
        updateObject(record, valuesByField);
    public SObject upsertRecord(sObject record, Map<SObjectField, Object> valuesByField){
        return (Account) upsertObject(record, valuesByField);
    public void deleteRecord(sObject record)


Then, in your test code you would need to instantiate the factory for each object, and set values in the Map using setFieldValue.

TestObjectFactoryContact contactFactory = new TestObjectFactoryContact();
		contactFactory.setFieldValue(Contact.Role__c, contactRole);
		contactFactory.setFieldValue(Contact.LastName,'child Contact');
		contactFactory.setFieldValue(Contact.Job_Level__c, contactLevel);
		Contact childContact = contactFactory.createRecord(contactValues,true);

NB: You will need to be careful about the Map (setFieldValue calls), since in all subsequent calls the values class variable will still contain its prior values. That could cause logic issues if you’re setting fields you didn’t intend to. The abstract class contains a clearFieldValues() method to clear it out if necessary.

Seems like.. That’s a Lot of Work For Not a Lot of Benefit?

No doubt, it’s wordy. But here’s why it’s worth it. The big benefit is that all your defaults/required fields are taken care of – if you had a required field on your Contact, the code would pre-fill it for you. Or, you can override the default by setting the value in the Map.

I created a custom metadata type that allows you to fill in defaults; if you look in the abstract class above, it calls a fillDefaults() method. This goes into the custom metadata and populates the required fields. For example, you can set the default Name for Accounts.

The setDefault method looks at this, and if you don’t override it, sets the Name field automatically on every record the test generator makes.

Now, in your abstract class, add this method:

public sObject fillDefaults(sObject record, Boolean isTest)
    	// Public so TestObjectFactoryTest can see it.
		String objectName;
		if (isTest) 
			objectName = 'TestObjectFactoryTest';
			objectName = getSObjectAPIName();
		SObjectType objectType = getSObjectType();
		String testName;

		if (defaults.size() == 0)
	    	defaults = 
	    			FROM Test_Class_Default__mdt
	    			WHERE Object__c = :objectName
	    			AND Field__c != null];
    	for (Test_Class_Default__mdt d : defaults)
    		Object value = null;
    		if (d.Type__c == 'Checkbox')
    			value = d.Checkbox_Value__c;
    		if (d.Type__c == 'Date')
    			value = d.Date_Value__c;
    		if (d.Type__c == 'DateTime')
    			value = d.DateTime_Value__c;		
	    	if (d.Type__c == 'Email')
	    		value = d.Email_Value__c;
	    	if (d.Type__c == 'Number')
	    		value = d.Number_Value__c;
	    	if (d.Type__c == 'Percent')
	    		value = d.Percent_Value__c;
	    	if (d.Type__c == 'Phone')
	    		value = d.Phone_Value__c;
	    	if (d.Type__c == 'Picklist')
	    		value = d.Picklist_Value__c;
	    	if (d.Type__c == 'Text')
	    		value = d.Text_Value__c;
	    	if (d.Type__c == 'Text Area')
	    		value = d.Text_Area_Value__c;
	    	if (d.Type__c == 'URL')
	    		value = d.URL_Value__c;

	    	// Get all the fields from the Object as sObjectField records
	    	// Then set the sObjectField value as a plain Object
	    	Map<String,Schema.SObjectField> mFields = objectType.getDescribe().fields.getMap();

	    	try {
	    		if (value != null && !isTest)
	    			testName = d.Text_Value__c;
	    	catch (System.Exception e){
	    		String error = 'TestObjectFactory: Unable to set default field ' + d.Field__c + ' to value ' + value + ' On Object ' +objectName + ' Error:' + e.getMessage();
	    		throw new MyException(error);
    	if (isTest)
    		return new Account(Name = testName);
    		return record;

The non-insert use cases are probably less valuable, but it least provides a consistent interface. For objects that don’t require other objects (like a parent Account) you can just use the factory.createObject(true); which will create the entire object, insert it, and return it to you.

Test Code Implications

Since this is an ordinary non-test class (has to be, since abstract is not available when @isTest) you need to create some test code to verify it actually works, and to maintain coverage requirements. My test class has a short and quick way to test each piece, for coverage purposes. If you do it carefully, you can set so you can find/replace the object name (i.e., change ‘Account’ to ‘Contact’) which can get you 80% of the way there.

private class TestObjectFactoryTest {

	@isTest static void AccountTest() {
		List<Account> accounts = new List<Account>();

		TestObjectFactoryAccount factory = new TestObjectFactoryAccount();

		// Create
		Account record = factory.createRecord(true,true);
		accounts = [SELECT Id, Name From Account WHERE Id = :record.Id];
		system.assertEquals(accounts.size(), 1);

		// Overloaded Create
		record = factory.createRecord(true);
		Accounts = [SELECT Id, Name From Account WHERE Id = :record.Id];
		system.assertEquals(Accounts.size(), 1);

		// Update
		factory.setFieldValue(Account.Name,'Update Test');
		accounts = [SELECT Id, Name From Account WHERE Id = :record.Id];
		system.assertEquals(accounts.size(), 1);
		system.assertEquals(accounts[0].Name,'Update Test');

		//Upsert with Insert
		Account upsertInsert = factory.createRecord(true);
		factory.setFieldValue(Account.Name,'Upsert Insert Test');
		record = (Account) factory.upsertRecord(upsertInsert);
		accounts = [SELECT Id, Name From Account WHERE Id = :record.Id];
		system.assertEquals(accounts.size(), 1);
		system.assertEquals(accounts[0].Name,'Upsert Insert Test');

		// Upsert with Update		
		factory.setFieldValue(Account.Name,'Upsert Update Test');
		record = (Account) factory.upsertRecord(record);
		accounts = [SELECT Id, Name From Account WHERE Id = :record.Id];
		system.assertEquals(accounts.size(), 1);
		system.assertEquals(accounts[0].Name,'Upsert Update Test');

		// Double check that we have 3 records now
		accounts = [SELECT Id, Name From Account ];
		System.assertEquals(accounts.size(), 3);

		// Delete
		accounts = [SELECT Id, Name From Account ];
		System.assertEquals(accounts.size(), 2);

		System.assertEquals(Account.SObjectType, factory.getSobjectType());


Like it? Hate it? Like I said, it’s wordy, but consistent, repeatable, and eliminates the need to think about default values. Feedback welcome below, or at @BobHatcher.Demand_Unit_Persona__cDemand_Unit_Persona__c

Salesforce: A Practical Approach to Queueables

Published by:

Salesforce triggers are great, but they run synchronously by default. What if you want to speed up the user experience and run noncritical tasks in the background? That’s what Queueable is for. But it’s not as simple as it’s made out to be.

I think most organizations have a simple process where they have a Trigger that calls a Trigger Handler. With a Queueable, you might think you can do something like this.


    if (Trigger.isBefore && Trigger.isUpdate) {
        ContactTriggerHandler.fireMyProcess(Trigger.oldMap, Trigger.newMap);

Trigger Handler:

public static void fireMyProcess(Map<Id,Contact>; oldMap, Map<Id,Contact>; newMap)
        List<Contact> contacts = new List<Contact>();
        for (Contact c : newMap.values())
            if (should be processed asynchronously)

        jobId = System.enqueueJob(new ContactTriggerHandlerQueueable(contacts)); 

Then a Queueable to implement your business logic.

 public class ContactTriggerHandlerQueueable implements Queueable {

        public ContactTriggerHandlerQueueable(List&lt;Contact&gt; contacts)
            // Save off my Contacts into a class variable

        public void execute(QueueableContext context) {
            // My business Logic

The problem with this approach is, as Brian Fear (aka sfdcfox) said in his outstanding response to my StackExchange Question: “The rule is that if you’re synchronous, you get 50 jobs for that transaction. Once you go asynchronous, you get only one child allowed.”

Brian went on to provide an example, which works great. Basically it detects if you’ve “Gone Synchronous” and runs the Queueable appropriately.

Every time I hear “Gone Synchronous” it makes me think of this.

This Works Great.. But

The issue you’ll soon run into is that you can’t chain Queueables in test classes. This is maddening, really. They give you this cool way to do asynchronous processing, but cut you off at the knees in mandatory test code.

The workaround is to detect if you are working in test, and run the Queueable synchronously instead.

if (!Test.isRunningTest()  || isBulkTest)
			QueueableUtilities.enqueueJob(new ContactTriggerHandlerQueueable(a, b, c, d));
			 ContactTriggerHandlerQueueable ctq = new ContactTriggerHandlerQueueable(a, b, c, d);

The only issue with this is that synchronous processing gets lower limits than asynchronous/queueable, which I found out when my bulk test code ran afoul of a CPU time limit. So in this case I created a public static Boolean variable in my Trigger Handler that I can set explicitly from the test class, that forces the test to run as a Queueable, above as isBulkTest. You just need to be careful that downstream chaining doesn’t do the same thing from the same test method.

Please let me know if this is helpful or if you have any questions. You can reach me at @BobHatcher on your friendly Twitter machine.

Salesforce: Check if Record Has Been Manually Edited

Published by:

We recently had a requirement in Salesforce that was a little unique. An ongoing automated process would update the associated Contact to a record, however they didn’t want the automated process to do the update if the field had been updated by a human. Makes sense – if someone takes the time to make a manual association of a Contact to a record, then it’s probably better information than the automatic matching algorithm.

As far as I can tell it’s impossible at runtime to know if the edit is due to a UI edit or is coming from code, so that’s out.

Here’s how we made it happen. The idea is to have a “last updated by code” checkbox that will be set by a workflow. But in order for it to know if it’s been updated by code, you need a 2nd checkbox that is only updated in Apex.

First, make two checkboxes on your record:

Set the latter to true by default when a new record is created.

When your code updates the record, set Contact_Change_Trailing_Lock__c = true.

Then, make two workflows.
-> Field Update – Contact_Last_Changed_By_Code__c = false

–> Field Update: Contact_Last_Changed_By_Code__c = true
–> Field Update: Contact_Change_Trailing_Lock__c = false

This results in you being able to use the Contact_Last_Changed_By_Code__c checkbox downstream in your logic to know not to overwrite the manual edit.