Handling System Errors in Unit Tests & Runaway Triggers

Red Argyle logo

How to write test coverage in salesforceIn my last post, “How to Write Test Coverage in Salesforce,” I went over how to isolate your test data. In this post, I am going to review how to handle system errors in unit tests and runaway triggers.

Now let’s move along to a quicker test tip, we need to create a user and then create some data and run a test as that user. You will get this error:

“MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Account: []”.

Salesforce Winter ‘14 release started to give me more issues in this area, and this StackExchange entry hits it on the nail. The issue is addressed in Salesforce Documentation, which delves into how DML on system objects can change access to a users records and DML on non system object or vice versa must be done in separate transactions. The simplest method is to wrap it in a different user run as method.

Account account = new Account(Name: 'Test Account');
insert account;
User user = new User(FirstName = ‘Test’......);
user.Custom_Account__c = account.Id;
System.runAs ( new User(Id = UserInfo.getUserId()) ) {
   insert user;
}

Look Out! Runaway Trigger!

When organizations in Salesforce continue to grow, there are more workflows and triggers that will occur in the system. Update triggers can run away and execute several times for the same execution context if you have the object updating in several places. Dan Appleman’s Advanced Apex Programming for Salesforce.com and Force.com has some good reading for the trigger execution and flow that can help with these issues. So the issue is:

public override void afterUpdate(Map<Id, SObject> oldSObjectMap) {
   system.debug(‘running after update trigger’);
   AccountService.doSomeProcess(this.records);
   AccountService.doSomeOtherProcess(this.records);
   ...
}

As the system grows, you may notice that diving into the logs you will see the ‘running after update trigger’ several times in one context, possible breaking if it loops too many times. This can be resolved with placing a static flag to allow only one execution to occur in a transaction, since static variable persist throughout a single transaction.

static public Boolean firstUpdate = true;
public override void afterUpdate(Map<Id, SObject> oldSObjectMap) {
   if(firstUpdate){
      firstUpdate = false;
      system.debug(‘running after update trigger’);
      AccountService.doSomeProcess(this.records);
      AccountService.doSomeOtherProcess(this.records);
      ...
   }
}

These little tricks will help you solve recurring issues that you will experience when writing good test coverage and avoiding future issues. Remember that if you make sure that all of your processes are tested and doing what they are supposed to, that 75% minimum becomes very easy to accomplish and you can feel confident about the code you deploy. So back to your Mt. Dew or cup of coffee, fellow devs, and happy coding!

Red Argyle logo
Red Argyle logo

Related Blog Posts