1

TL;DR

How can I write to non-writeable SObject fields while running apex unit tests?

Background

While creating unit tests that check a method's functionality independent of any DML functions, it is sometimes necessary (or at least very helpful) to set field values on SObjects.

Example:

Lets say I have the class shown below. (In this example, my org's Account object has a formula field Owners_Email_Address__c which, returns the email address of the Owner of the Account.)

public with sharing class MyClass{

    public static String getParentAccountsOwnerEmail(Contact myContact){
        return (String)myContact.Account.get('Owners_Email_Address__c');
    }

    public static Boolean createdDateMatchesValue(Contact myContact, Date expectedCreatedDate){
        return myContact.CreatedDate == expectedCreatedDate;
    }
}

While writing my apex class, I could get both the Owners_Email_Address__c and CreatedDate values populated, but doing so would require multiple DML operations:

  • create and insert User for Account owner
  • create and insert Account with new User as Owner
  • create and insert Contact then use Test.setCreatedDate() to specify a date (if something different than the current date needed)

While this is all fine, it often runs into other issues that cause additional considerations and overhead, such as:

  • Making sure other required field on each desired record to insert are properly set
  • Verifying that a desired field wasn't changed during the DML operation due to another automation

As an org grows and its automations grow, this can make inserting just a few records for a test quite a headache.

Question

When DML operations aren't necessary for a test (such as when building unit tests that should only test the functionality of a specific function), how can I bypass the need to have multiple DML operations to populate non-writeable fields?

1 Answer 1

1

You can create an SObject record with specified read-only field values using JSON.deserialize(), as long as you do not need to actually run any DML statements (insert, update, delete) on those records.

The setup for this will follow something like this:

MySObjectType myRecord = (MySObjectType) JSON.deserialize(jsonString, MySObjectType.class);

And your jsonString will look something like this:

'{"attributes":{"type":"MySObjectType"},"My_Read_Only_Field":"My test-desired value"}'

An example from the question:

String desiredEmail = '[email protected]';
String accountString = '{"attributes":{"type":"Account"},"Owners_Email_Address__c":"'+desiredEmail+'", "CreatedDate" : "2000-01-01"}';
Account testAccount = (Account) JSON.deserialize(accountString, Account.class);

// These should both pass
Assert.areEqual(desiredEmail, testAccount.Owners_Email_Address__c);
Assert.areEqual(Date.newInstance(2000, 1, 1), testAccount.CreatedDate);
1
  • 1
    I recommend you take a look at SObjectFabricator for a clean API that uses this technique under the hood, as mentioned here.
    – Phil W
    Commented Sep 9, 2023 at 10:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .