I have a trigger with two events, Before Insert and Before Update. They are working fine when I make an object or update an object in org, but when I'm trying to create a object and update that object in testclass then only insert is working update is not working. If I remove before insert event from trigger then before update is working.
[before insert + before update = before insert working but before update not working],
individually ,
[before insert = working ]
[before update = working ]
I am using a trigger template,
Here is the code :
Trigger :
trigger CompTrigger on Comp__c (before insert , before update)
{
new TriggerTemplateV2.TriggerManager(new TriggerHandler()).runHandlers();
}
Handler :
public with sharing class TriggerHandler extends TriggerTemplateV2.TriggerHandlerAdapter {
// Will be called before insertion of object
public override void onBeforeInsert (List<sObject> newList)
{
system.debug('IN onBeforeInsert');
}
// Will be called before updation of object
public override void onBeforeUpdate (List<sObject> newList, Map<Id, sObject> newMap, List<sObject> oldList, Map<Id, sObject> oldMap)
{
system.debug('IN onBeforeUpdate');
}
}
/**
* Helper method to get the class name for a given Object
* Used as the key identifier of the trigger handler classes
*/
private String getClassName(Object o)
{
String className = null;
if(o != null)
{
// The value looks something like this: "ProjectTriggerHandler:[idToOldProjectMap=null, newProjectList=null]"
String objectAsString = String.valueOf(o);
className = objectAsString.substring(0,objectAsString.indexOf(':'));
}
return className;
}
}
/**
* Interface definitions for the before and after trigger handlers
*
*/
public interface BeforeTriggerHandler
{
void onBeforeInsert (List<sObject> newList);
void onBeforeUpdate (List<sObject> newList, Map<Id, sObject> newMap, List<sObject> oldList, Map<Id, sObject> oldMap);
void onBeforeDelete (List<sObject> oldList, Map<Id, sObject> oldMap);
}
public interface AfterTriggerHandler
{
void onAfterInsert (List<sObject> newList, Map<Id, sObject> newMap);
void onAfterUpdate (List<sObject> newList, Map<Id, sObject> newMap, List<sObject> oldList, Map<Id, sObject> oldMap);
void onAfterDelete (List<sObject> oldList, Map<Id, sObject> oldMap);
void onAfterUndelete (List<sObject> newList, Map<Id, sObject> newMap);
}
/**
* An abstract adapter class that can be extended for convenience. By extending
* this class only the relevant handler methods need to be implemented.
*/
public abstract class TriggerHandlerAdapter implements BeforeTriggerHandler, AfterTriggerHandler
{
public virtual void onBeforeInsert (List<sObject> newList) {}
public virtual void onBeforeUpdate (List<sObject> newList, Map<Id, sObject> newMap, List<sObject> oldList, Map<Id, sObject> oldMap) {}
public virtual void onBeforeDelete (List<sObject> oldList, Map<Id, sObject> oldMap) {}
public virtual void onAfterInsert (List<sObject> newList, Map<Id, sObject> newMap) {}
public virtual void onAfterUpdate (List<sObject> newList, Map<Id, sObject> newMap, List<sObject> oldList, Map<Id, sObject> oldMap) {}
public virtual void onAfterDelete (List<sObject> oldList, Map<Id, sObject> oldMap) {}
public virtual void onAfterUndelete (List<sObject> newList, Map<Id, sObject> newMap) {}
}
}
TestClass :
@isTest
public class Test_TriggerHandler {
static testmethod void TriggerHandler_Test()
{
system.debug('in test method');
Test.startTest();
Comp__c comp = new Comp__c();
insert comp;
cmp.Status__c = 'failed' ;
update comp;
Test.stopTest();
}
}
Best Answer
This appears to be a flaw in your trigger framework's design.
Your controlling variables,
enabledTriggerEventName
,isInitialized
,enabledBeforeHandlerClassName
, andenabledAfterHandlerClassName
are all static.The first time you issue a DML call in your test class,
isInitialized
is false, and you go through some extra setup in your constructor to setenabledBeforeHandlerClassName
,enabledAfterHandlerClassName
, andenabledTriggerEventName
.The enabledbefore/after handler class names (the static ones) aren't really a concern in this particular instance, but I suspect it'll cause you issues once you have more than one SObject's trigger using your framework.
The issue in this particular case lies with
enabledTriggerEventName
.Still on your first DML call in your test class, the
insert
call,enabledTriggerEventName
is set toinsert
. YourisInitialized
static variable is set totrue
, and the constructor call finishes.You then move on to the
runHandlers()
call.allTriggersDisabled
is false, so you pass the first if statement.Trigger.isBefore
is true,beforeHandler
is not null, andenabledBeforeHandlerClassName
is equal tobeforeHandler
, so you pass the second if statementTrigger.isInsert
is true, andenabledTriggerEventName
== 'insert', so you pass the the third if statementAt this point, your actual handler code is called, and you get your debug statement printed for the insert event. Your insert dml in your test method completes.
We now move on to your update DML call.
isInitialized
, being static, is still true since your insert and update calls are in the same transaction. Thus,enabledBeforeHandlerClassName
,enabledAfterHandlerClassName
, your invocation lists, andenabledTriggerEventName
are not updated.Your constructor completes, and we move to calling
runHandlers()
.allTriggersDisabled
is false, so you pass the first if statement.Trigger.isBefore
is true,beforeHandler
is not null, andenabledBeforeHandlerClassName
is equal tobeforeHandler
, so you pass the second if statement (N.B. this is where you'll run into issues when you have two different triggers using your framework that run in the same transaction)Trigger.isInsert
is false, so you fail the third if statement, and move on to the first else ifTrigger.isUpdate
is true, butenabledTriggerEventName
still == 'insert' (since you didn't re-run thatisInitialized
block), so you fail this else if statementThat explains why your insert is working, but your update is not...or rather, why your first DML call will work, but all subsequent DML calls that use your framework (and the same type of DML, e.g. insert/update/delete) will fail.
I haven't completely digested your code (and I have my own work to attend to), but if I had to take a guess, I'd guess that the initialization block in your constructor is to handle cases where you have more than 200 records being DML'd (and Salesforce breaks them up into chunks with one execution per 200 records). If that's the case, fixing this will entail finding a better mechanism to control what happens in that situation.
To be useful, your framework will absolutely need to change
enabledTriggerEventName
,enabledBeforeHandlerClassName
, andenabledAfterHandlerClassName
more than once.