How to mock formulas and read-only fields in Apex tests?
Tags: apex, testing, performance, mock
Published: 2022-03-25 22:07:46

_Learn how to use dependency injection to mock read-only fields, such as formula fields and child relationships in your tests._


A while ago we explored the [Mock Data Layer Pattern](https://github.com/gitmatheus/Mock-Data-Layer-Pattern), which allows us to mock virtually any relationships between records within your tests. This is especially handy when trying to improve the performance of these tests - more details [here](https://dev.to/matheusgoncalves/unit-test-can-we-mock-relationships-in-salesforce-apex-4ohb).

Now, how can we mock formula fields and <strong>read-only</strong> child relationships if they are, well, read-only fields? 🤔

## Mocking values in read-only fields

For this exploratory project, we will use a *not-so-realistic* scenario with parent and child accounts, but the intent is to create a test for the Account Trigger Handler that will use a <strong>Mock Field Reader</strong> and a <strong>Mock Data Layer</strong>.

The <strong>Mock Field Reader</strong> will be used so we can mock two <strong>read-only</strong> fields on the Account object.

- `Child_Accounts__r`, a read-only child relationship on the parent record
- `External_URL__c`, a formula field on the child record

The trigger handler logic will update the following field:
- `Parent.Latest_Child_URL__c`

Based on the following formula field:
- `Child.External_URL__c`

---

## ⚙️ Files used in this project

```
force-app
    main
        default
            classes
                ◦ AccountTriggerHandler
                ◦ AccountTriggerHandlerTest
                ◦ FieldReader
                ◦ IFieldReader
                ◦ MockFieldReader
                ◦ TestUtils
            triggers
                ◦ AccountTrigger
```

---

### ⚡️ Show me the code!

#### Getters

To respect the injected dependency, these methods must be present on both the real instance of the field reader ( `FieldReader` ) and the mock instance of the field reader ( `MockFieldReader` ).

`Object getFieldValue(SObject record, String fieldName)`

This method is used to retrieve the mock values of formula fields. It returns an `Object` that can later be casted as different types, such as `String`, `Number`, `Id`, etc. 

`SObject getFieldRecord(SObject record, String fieldName)`

This method is used to retrieve the mock values that represent a single record. It returns an `SObject`.

`List<SObject> getFieldRecords(SObject record, String fieldName)`

This method is used to retrieve the mock values that represent a list of records, such as a child relationship. It returns a list of `SObjects`.

#### Setter

There's only one method used to set the values, and it's only present on the mock instance of the field reader ( `MockFieldReader` ).

`addValueToField(SObject record, String field, Object value)`

This method adds the field/value pair to a map of mocked values, indexed by the record Id.

#### Examples

In the <a href="https://github.com/gitmatheus/MockFieldReader" target="_blank" rel="noopener">repository on Github</a> you will find a trigger handler class and its respective test class with the usage of this Mock Field Reader. 

<strong>Snippet from the AccountTriggerHandler</strong>: pay attention to how the following methods are being used:

`fieldReader.getFieldRecords(parentAndChildAccount, 'Child_Accounts__r');`
    
`fieldReader.getFieldValue(latestChild, 'External_URL__c');`

{% embed https://gist.github.com/gitmatheus/a9dc6cf183893d6b482ec3b43045a4f6.js %}

And in the <strong>AccountTriggerHandlerTest</strong> you can see how these read-only fields can be mocked, using the method `addValueToField`: 

{% embed https://gist.github.com/gitmatheus/d76a07c988610de09e2775eac3c8cb80.js %}

With this code and the Mock Field Reader, it becomes easy to mock read-only fields used in the tests. 

I hope that helps!

_Originally published on [matheus.dev](https://matheus.dev/how-to-mock-formulas-and-non-writable-relationships-in-apex/)_