0

can some one tell me how to cover custom metadata in test class? in particular if (Trigger_Disabler__mdt.getInstance('Account_Disabler')?.Disable_Trigger__c == true) line in the code below.

  • Context here
  1. Created Trigger_Disabler__mdt Custom metadata with a checkbox field Disable_Trigger__c.
  2. Account_Disabler is the record on custom metadata.
  3. Account Trigger code gets the custom metadata record and checks to see if Disable_Trigger__c is true or false.
trigger Account_Trigger on Account (after update) {
    
    //Custom metadata to turn on/off Trigger
    if (Trigger_Disabler__mdt.getInstance('Account_Disabler')?.Disable_Trigger__c == true){
        return;
    }
    else{
        if(trigger.IsAfter && trigger.isUpdate){
           AccountTriggerHandler.publishNetSuiteSyncEvent(Trigger.New, Trigger.OldMap); 
        }
    }
        
    }
2
  • 2
    As a side note, the else isn't needed here. A return immediately goes back to the next level up in the stack, which in this case would terminate the trigger. Also, if you've never heard of YAGNI (You Ain't Gonna Need It), this trigger only has one event, so you don't need to check if you're in that event. Best to wait until you know you're going to need it before you add it in.
    – sfdcfox
    Mar 15 at 14:28
  • Thanks @sfdcfox for YAGNI information and also I corrected my code, removing if else. Mar 19 at 2:48

2 Answers 2

4

You have here a great example of why you should not put your code in a trigger body, but rather in a class. If you put this flag in your handler instead, you can just hook it up to a static variable that can be overridden in tests.

public class AccountTriggerHandler
{
    @TestVisible static Boolean bypassTrigger = Trigger_Disabler__mdt.getInstance(
        'Account_Disabler'
    )?.Disable_Trigger__c == true;
}

Then, in your tests, all you need to do is set AccountTrigerHandler.bypassTrigger = true.

2
1

Another way to handle this would be to make a proxy class that is responsible for accessing Custom Metadata Type records (instead of accessing them directly). Having this additional layer gives you the flexibility to make/use in-memory records of your __mdt object (which because they're metadata, we can't insert/update in a test class). As a simple example of such a proxy class (which you will need to modify).

public class MyMDTProxy{
    private static Map<String, MyMDTType__mdt> mdtRecordsByDeveloperName;

    // This is a static initialization block, which we can basically think of as
    //   a "static constructor"
    static{
        mdtRecordsByDeveloperName = new Map<String, MyMDTType__mdt>();
    
        // This step doesn't need to be done here, but you do need a way to get the
        //   "actual" MDT records somewhere in the proxy for it to be useful outside
        //   of unit tests.
        // Making this a separate line (instead of just using this to initialize the
        //   map in the first place) helps guard against situations where you don't have
        //   any records for the MDT in your target org
        mdtRecordsByDeveloperName.putAll(MyMDTType__mdt.getAll());
    }

    public static void addRecord(MyMDTType__mdt givenRecord){
        mdtRecordsByDeveloperName.put(givenRecord.DeveloperName, givenRecord);
    }

    public static MyMDTType__mdt getInstance(String developerName){
        return mdtRecordsByDeveloperName.get(developerName);
    }
}

Usage would be basically the same as what you have now

// instead of Trigger_Disabler__mdt
if (TriggerDisablerProxy.getInstance('Account_Disabler')?.Disable_Trigger__c == true){

And setting this up in your test class is simple

// Since Salesforce clears out static variables between each test, we can't
//   do this in an @SetupTest annotated method.
// It just needs to be a static method that gets called in each test method
static void mdtSetup(Boolean enabled){
    // Because we're  internally using a map and initializing with existing MDT
    //   records first, you can easily "override" the MDT record for tests
    TriggerDisablerProxy.addRecord(new Trigger_Disabler__mdt(
        DeveloperName = 'Account_Disabler',
        Disable_Trigger__c = enabled
    );
}

@isTest
static void triggerDisabled_doNoWork(){
    mdtSetup(false);

    insert new Account();
}

@isTest
static void triggerEnabled_doWork(){
    mdtSetup(true);

    insert new Account();
}

This is more work than Adrian's approach of using a @TestVisible variable in a handler class.

In either approach though, you do want to make sure that you're keeping as much logic out of your actual trigger as possible.

2
  • 100% agree. All my CMDT is wrapped by a proxy class so I can mock CMDT for test isolation
    – cropredy
    Mar 15 at 15:51
  • Thanks for your answer. Mar 19 at 3:05

You must log in to answer this question.

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