0

THE PROBLEM: My CustomObjectTrigger has a CustomObjectHandler class. I have code that is fired in the CustomObjectHandler class when the CustomObjectTrigger is fired by a new record that (isInsert && isAfter). I have created a test class to test the code, but the test code only seems to trigger (isInsert && isBefore). Something must be incorrect in my test class.

Any help would be greatly appreciated!

Trigger:

trigger CustomObjectTrigger on CustomObject__c(after delete, after insert, after undelete, after update, before delete, before insert, before update) {

    if (CustomObjectHandler.hasExecuted) {
        return;
    }

    CustomObjectHandler.hasExecuted = true;

    CustomObjectHandler = new CustomObjectHandler(Trigger.isExecuting, Trigger.size);

    if(Trigger.isInsert && Trigger.isBefore) { // this code runs
        handler.OnBeforeInsert(Trigger.new);
    }

    else if(Trigger.isInsert && Trigger.isAfter) { // this code doesn't run
        handler.OnAfterInsert(Trigger.new);
        CustomObjectHandler.OnAfterInsertAsync(Trigger.newMap.keySet());
    }
}

Test Class:

@isTest
public class LookupRollup_CustomObject__c_test {

    static void test_method_one() {

        Test.startTest();

        // Create Custom Object
        CustomObject__c rs = new CustomObject__c();
        rs.Start_Date__c = system.today();
        rs.End_Date__c = system.today() + 30;
        rs.Annualized_Rent__c = 12000;
        String testAfterTrigger = rs.Id;
        insert rs;

        List<CustomObject__c > testInsert = new List<CustomObject__c >([SELECT Id, Annualized_Rent__c FROM CustomObject__c WHERE Id =: rs.id]);
        system.assertEquals(testInsert[0].Annualized_Rent__c, rs.Annualized_Rent__c);

        rs.Annualized_Rent__c = 12001;
        update rs;

        Test.stopTest();

        }
    }

}
  • I've tried multiple ideas, such as adding the rs.Id into the //Create Custom Object section of the test class prior to insert. I was assuming this would make it an after trigger event since the Id is assigned after insert, but this did not fix the problem. No errors in my code though... – paulK Feb 14 at 1:46
  • 1
    Shouldn't you be setting CustomObjectHandler.hasExecuted = false after your before insert block is executed successfully? Or else, the value being true in the next pass, the control will just return without executing the trigger. – Jayant Das Feb 14 at 1:51
2

Well, it's this thing here.

if (CustomObjectHandler.hasExecuted) {
    return;
}

CustomObjectHandler.hasExecuted = true;

That code executes in before insert, and then CustomObjectHandler.hasExecuted is true for the rest of the transaction, including all other trigger events that should happen to execute during its course. (I'm assuming that's a static Boolean variable).

Static Booleans are a very poor way to do recursion control that cause failures in a wide variety of situations, including

  • this one.
  • cases where more than 200 records are processed in a single transaction.
  • situations involving allOrNone=false.

All of those situations involve the trigger executing more than once in a single transaction. This particular implementation, which ensures only one trigger event can execute per transaction, is more problematic.

  • OMG, THANK YOU. I've been staring at this code for an hour. David, I was using a static boolean variable. What do you recommend for recursion control? Do you have somewhere you can point me to for recursion control best practices? – paulK Feb 14 at 1:59
  • 1
    I believe it's covered in an excellent blog by cropredy. I'll see if I can find the link. – David Reed Feb 14 at 2:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.