function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Mel LisauMel Lisau 

How can i have a test that runs for Change trigger and Realtime trigger on the same Salesforce entity ?

I have a change trigger on  Account. I alos have a realtime triger on Account. If i run bothh tests then only one passes and i get an error for the second because if the change test has ran , then the realtime triggers and is expecting certain values which are not present.
Is there a way I can run each independently , so if I run a change for Account , ignore any other Account trigger and run just for that trigger test. 
Or can i run all test on an entity , regardless if change or realtime as one object ?
Best Answer chosen by Mel Lisau
Mel LisauMel Lisau
Hi , 
Bit confused with that approach , but if i share some code maybe it may be a bit clear. Sorry probably my fault for not expalining clearly:

Triggers:

First Trigger
Trigger TestTrigger_1C_I on Account (after insert) {
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
TestTrigger_Account__c change = new TestTrigger_Account__c();
change.Action__c = 'insert'; 
change.Link__c = id; 
change.Map__c = '1C'; 
insert change;
}
}

Second Trigger
Trigger TestTrigger_3C_I on Account (after insert) {
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
TestTrigger_Account__c change = new TestTrigger_Account__c();
change.Action__c = 'insert'; 
change.Link__c = id; 
change.Map__c = '3C'; 
insert change;
}
}


Third Trigger
Trigger TestTrigger_4C_I on Account (after insert) 
{  
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
TestTrigger_Account__c change = new TestTrigger_Account__c();
change.Action__c = 'insert'; 
change.Link__c = id; 
change.Map__c = '4C'; 
insert change;
}
}

Tests:
I used to have indicidual test for each lke below but am trying to make one for all but control which trigger will run 

public static testMethod void MyTest_Chng_Account_I() 
   { 
            
        List<Account> AccountList = new List<Account>();
        Account Account_0 = new Account( Name='Test123');
        AccountList.add(Account_0);
        Account Account_1 = new Account( Name='Test456');
         AccountList.add(Account_1);
        insert AccountList;

        TestTrigger_Account__c Account_list = [SELECT Id, Link__c, Action__c, Processed__c, Map__c from TestTrigger_Account__c WHERE Link__c = :AccountList[0].Id and Map__c = '1C' limit 1]; 
        System.assertEquals(Account_list.Link__c, AccountList[0].Id);
        System.assertEquals(Account_list.Map__c, '1C');
        System.assertEquals(Account_list.Action__c, 'insert');                
}
}

The above may fail as it dont know which trigger will run ... 

TestTrigger_Account__c is my custom table .as soon as there is an insert of account , data is added to the custom table and i do my check.

  1. How do i know which trigger ran , it could have been the one which result in '4C' ?
   2 Can i add an assert for the others here ?


    


 

All Answers

SUCHARITA MONDALSUCHARITA MONDAL
Hi Mel,
Always follow best practice on Trigger i.e.  ONE TRIGGER PER OBJECT.
If you have 2 triggers on same Object, you never get to know which trigger runs first that is exactly happening in your case. Keep one trigger and different methods for different processing to perform. In that way you can write your test cases to cover your trigger.

Hope it'll give some direction to you!

Thanks,
Sucharita
Mel LisauMel Lisau
Hi Sucharita
I have read best practices, but in my case lets take the object Account,   I have 3  triggers for "after insert ", "after update"  "before update" each trigger has a unit test.  Does Apex unit testing have a flag which only allows you specify the trigger you are targeting ?

Thanks
SUCHARITA MONDALSUCHARITA MONDAL

Hi Mel,

You don't have to write 3 Triggers, just put three methods which runs on  "after insert ", "after update"  "before update". As below

Trigger AccountTrigger on Account (after insert, after update, before update){
    if(Trigger.isBefore){
        if(Trigger.isInsert){
            TriggerHandler.beforeInsert(Trigger.New);  //here TriggerHandler is handler class of Trigger and will run only in Before Insert
        }
        if(Trigger.isUpdate){
            TriggerHandler.beforeUpdate(Trigger.New);
        }
    }
    if(Trigger.isAfter){
        if(Trigger.isInsert){
            TriggerHandler.afterInsert(Trigger.New);
        }
        if(Trigger.isUpdate){
            TriggerHandler.afterUpdate(Trigger.New);
        }
    }
}

Mel, in test class you define the instance to identify which class is called.
TriggerHandler th = new TriggerHandler();
th.beforeInsert(ListOfRecords);    // that's how you cover methods using test class


Hope this helps!

Share your thoughts!

Thanks,
Sucharita

Mel LisauMel Lisau
Hi thanks for the reply.
I have set out below my scenario. The triggers are dynamically generated so I can generate one and then maybe later generate another, so not all at once 
I have 3 triggere for example:

//Trigger1

Trigger MyTriggerCh_1C_tr_I on Account (after insert) {
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
MyTriggerCh_Account__c diff = new MyTriggerCh_Account__c(); // custom fields are set
diff.custfield1__c = 'insert'; 
diff.custfield2__c = id; 
insert diff;
}
}

//Trigger2

Trigger MyTriggerCh_3C_tr_I on Account (after insert) {
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
MyTriggerCh_Account__c  diff = new MyTriggerCh_Account__c();
diff.custfield1__c = 'insert'; 
diff.custfield2__c = id; 
insert diff;
}
}


//Trigger3
Trigger MyTrigger_Account_U on Account (after update) {
set<ID> ids = Trigger.newMap.keyset(); 
for (Account newAcc: Trigger.new) 
{
Account oldObject = Trigger.oldMap.get(c.ID);
if (newAcc.AccountNumber!= oldObject.AccountNumber )
{
    MyTrigger_SC_Proc.HandleProc('Account','update',ids); } // HandleProc is in another class.

}

I have test for each trigger . The issue is if the first test for trigger 1 runs , is an "after insert", so trigger2 could run but  i dont want it to run as of yet until trigger 1 has finished running.. hence I get only 1 test out of 3 passing.
 
Mel LisauMel Lisau
and alo Trigger 3 could potentially run after an update on the Account entity, which i dont want until Trigger 1 has finished. My test actually take into account these. I actually do an insert into Account and do the assertions to chekc .. individually it all runs fine, but once there are multiple it gets a bit tricky.   I think there must be a way. I prefer not to modify the trigger code ..... 
SUCHARITA MONDALSUCHARITA MONDAL
Hi Mel,
You can go with something below: by keeping only ONE TRIGGER

Trigger MyTriggerCh_1C_tr_I on Account (after insert, after update) {
    List<Account> accList = new List<Account>();
    List<Account> newAccList = new List<Account>();
    for(Account acc : Trigger.new){
        if(Trigger.isAfter){
            if(Trigger.isInsert){
                Account newacc = new Account();
                newacc.custfield1__c = 'insert';
                newacc.custfield2__c = acc.Id;
                accList.add(newacc);
            }
             if(Trigger.isUpdate){
                 if(acc.Account_Number__c != acc.Trigger.OldMap.get(acc.Id)Account_Number__c){
                    newAccList.add(acc.Id);                    
                 }
            }
        }
    }
    if(accList.size()>0){
        insert accList;
    }
    if(newAccList.size()>0){
        TriggerHandler.HandleProc('Account','Update',newAccList);
    }
    
}

public class TriggerHandler{  //Create handler classes for further processing 
    public static void HandleProc(String acc, String str, List<Id>recIds){
        //do code
    }
}


P.S. Here are some points for above code:
1. It's never a good practice to keep 3 triggers on one object, you'll always get problem. Also when LOC (lines of code) increases, it gets worse.
2.  Trigger context variables have it's own importance means code will run when condition meets i.e. if context variable is writter as Trigger.isBefore then code inside the scope will run on every before (be it on insert or update or delete)
3. Always try to use List/collections for making DML (i..e insert/delete operation), otherwise you'll hit salesforce governer limit ( as you have in your first and second trigger)
4. Learn about Trigger framework

I hope you got some understanding.

Share your thoughts!

Thanks,
Sucharita
 
Mel LisauMel Lisau

Hi , thankyou so much for your patience with this. I do understand what you are saying, but I prefer not to change the triggers but was thinking of a Test Object. So for example I have 2 after insert on account and if in my test I was to insert an account as follows:

List<Account> AccountList = new List<Account>();
Account Account_0 = new Account( Name='Test123');
AccountList.add(Account_0);
insert AccountList;

If I have 2 triggers , what will essentially happen is that there will be 2 entries as each trigger is fired, which one it is I wont know. So my unit test check may fail.
So was thinking of a Global test object/class that can incorporate all "after insert" etc per entity . Is that in your opinion possible ?

SUCHARITA MONDALSUCHARITA MONDAL
Hi Mel,

Try with following:
I've passed list if you want for single record then pass accList.[0].  But it's always a good practice to test your code with bulk of records.
You can change method name based on your method name.

@isTest
public class testClass{
    @isTest
    public static void test1(){
        List<Account> accList = new List<Account>();
        Account parentAcc = new Account();
        parentAcc.name = 'Parent';
        insert parentAcc; 
        
        for(int i=0;i<200;i++){
            Account acc = new Account();
            acc.name = 'test'+i;
            acc.custfield1__c  = 'insert';
            acc.custfield2__c = parentAcc.id;
            accList.add(acc);    
        }
        Test.startTest();
        insert accList;
        TriggerHandler.HandleProc('Account','Update',accList);  // change it by your method and pass single record.
        Test.stopTest();
    }
}

Share your thoughts!

Thanks,
Sucharita
Mel LisauMel Lisau
Hi , 
Bit confused with that approach , but if i share some code maybe it may be a bit clear. Sorry probably my fault for not expalining clearly:

Triggers:

First Trigger
Trigger TestTrigger_1C_I on Account (after insert) {
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
TestTrigger_Account__c change = new TestTrigger_Account__c();
change.Action__c = 'insert'; 
change.Link__c = id; 
change.Map__c = '1C'; 
insert change;
}
}

Second Trigger
Trigger TestTrigger_3C_I on Account (after insert) {
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
TestTrigger_Account__c change = new TestTrigger_Account__c();
change.Action__c = 'insert'; 
change.Link__c = id; 
change.Map__c = '3C'; 
insert change;
}
}


Third Trigger
Trigger TestTrigger_4C_I on Account (after insert) 
{  
set<ID> ids = Trigger.newMap.keyset(); 
for(ID id : ids) 
{
TestTrigger_Account__c change = new TestTrigger_Account__c();
change.Action__c = 'insert'; 
change.Link__c = id; 
change.Map__c = '4C'; 
insert change;
}
}

Tests:
I used to have indicidual test for each lke below but am trying to make one for all but control which trigger will run 

public static testMethod void MyTest_Chng_Account_I() 
   { 
            
        List<Account> AccountList = new List<Account>();
        Account Account_0 = new Account( Name='Test123');
        AccountList.add(Account_0);
        Account Account_1 = new Account( Name='Test456');
         AccountList.add(Account_1);
        insert AccountList;

        TestTrigger_Account__c Account_list = [SELECT Id, Link__c, Action__c, Processed__c, Map__c from TestTrigger_Account__c WHERE Link__c = :AccountList[0].Id and Map__c = '1C' limit 1]; 
        System.assertEquals(Account_list.Link__c, AccountList[0].Id);
        System.assertEquals(Account_list.Map__c, '1C');
        System.assertEquals(Account_list.Action__c, 'insert');                
}
}

The above may fail as it dont know which trigger will run ... 

TestTrigger_Account__c is my custom table .as soon as there is an insert of account , data is added to the custom table and i do my check.

  1. How do i know which trigger ran , it could have been the one which result in '4C' ?
   2 Can i add an assert for the others here ?


    


 
This was selected as the best answer
Mel LisauMel Lisau
I have been doing some reading and found the following 
https://www.sfdc99.com/2015/01/19/the-one-trigger-per-object-design-pattern/
I am trying to see how this can be implemented in my approach .. 
What is a bit confusing is I have 3 different pieces of code for after insert in 3 different triggers , so do they all need to go there ?
SUCHARITA MONDALSUCHARITA MONDAL
Mel,
There is nothing to get confused. Just keep ONE TRIGGER PER OBJECT as you'll never get to know the order of execution between trigger.
Try to move your logic based on the conditions (I've already provided you the sample code along with test class)

Just give it a try and you'll lend with a solution.
Share your thoughts!

Thanks,
Sucharita
Mel LisauMel Lisau
YES, perfect I  have tried and implmented it. All is good now. 
Thanks so much for the help and patience with this.

cheers