Reply
Regular Contributor
mgodsey
Posts: 85
0
Accepted Solution

Unit Test Failing Because of Maximum Trigger Depth Exceeding

I'm trying to write a unit test for a trigger that captures the OwnerID of an Opportunity, and then populates a "Owner Details" field (giving us access to their entire user record) and a "Sales Manager" field (which is the user's manager).

 

I'm at 73% coverage, but the part that is not covered is the after update when the Opportunity Owner has changed. I've written into my test a scenario when the Opp Owner changes, but I'm getting the following error message:

 

System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, CopyOwnerInformation: execution of AfterUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 006S0000006jI1iIAE; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, CopyOwnerInformation: maximum trigger depth exceeded Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i] Opportunity trigger event AfterUpdate for [006S0000006jI1i]: [] Trigger.CopyOwnerInformation: line 40, column 1: []

 

Here is the relevant code from my trigger. This is the part that I am trying to cover:

 

//Insert new owner details ID if it has changed
if(opp.Owner_Details__c == null || (opp.OwnerId != oldopp.OwnerID)){
List<Opportunity> OppsWithNewOwner = [SELECT ID, OwnerID, Owner_Details__c FROM Opportunity WHERE id in:trigger.new];

for (Integer i=0; i<OppsWithNewOwner.size(); i++){                                      
    OppswithNewOwner[i].Owner_Details__c = OppsWithNewOwner[i].OwnerID;}
    
    update OppsWithNewOwner;

    }

 

And here is my test class:

 

@isTest
private class Test_CopyOwnerInformation{
    static testMethod void CopyOwnerInformation(){
    
//set up test records
        
User user1 = new User();
        user1.firstname = 'Test User 1 First Name';
        user1.lastname = 'Test User 1 Last Name';
        user1.alias = 'user1';
        user1.email = 'testuser@unittest.com';
        user1.EmailEncodingKey = 'ISO-8859-1';
        user1.username = 'testuser1@unittest.com';
        user1.TimeZoneSidKey = 'America/Chicago';
        user1.LocaleSidKey = 'en_US';
        user1.ProfileId = '00e70000000sP2v';
        user1.LanguageLocaleKey = 'en_US';
        insert user1;
        
User manager2 = new User();
        manager2.firstname = 'Test Manager 2 First Name';
        manager2.lastname = 'Test Manager 2 Last Name';
        manager2.alias = 'mgr2';
        manager2.email = 'testmanager@unittest.com';
        manager2.EmailEncodingKey = 'ISO-8859-1';
        manager2.username = 'testmanager2@unittest.com';
        manager2.TimeZoneSidKey = 'America/Chicago';
        manager2.LocaleSidKey = 'en_US';
        manager2.ProfileId = '00e70000000sP2v';
        manager2.LanguageLocaleKey = 'en_US';
        insert manager2;
        
User user2 = new User();
        user2.firstname = 'Test User 2 First Name';
        user2.lastname = 'Test User 2 Last Name';
        user2.alias = 'user2';
        user2.email = 'testuser@unittest.com';
        user2.EmailEncodingKey = 'ISO-8859-1';
        user2.username = 'testuser2@unittest.com';
        user2.manager = manager2;
        user2.TimeZoneSidKey = 'America/Chicago';
        user2.LocaleSidKey = 'en_US';
        user2.ProfileId = '00e70000000sP2v';
        user2.LanguageLocaleKey = 'en_US';
        insert user2;
  
Account account = new Account();
        account.Name = 'Test Account';
        account.OwnerID = user1.id;
        account.web_site__c = 'test.com';
        account.industry = 'Entertainment';
        account.type = 'Advertiser';
        insert account;
        
Opportunity opportunity = new Opportunity();
        opportunity.AccountId = account.Id;
        opportunity.OwnerID = account.OwnerId;
        opportunity.Name = 'Test Opportunity';
        opportunity.StageName = 'Active';
        opportunity.CloseDate = System.today();
        opportunity.Amount = 1000.00;
        opportunity.Type = 'New Client';
        
//test  
    test.startTest();     
        insert opportunity;
    
//Validate that the Owner Details field was updated.
    opportunity = [SELECT OwnerId, Owner_Details__c, Owner_Details__r.ManagerID, SalesManager__c FROM Opportunity WHERE id = :opportunity.id];
    System.assertEquals(opportunity.OwnerId, opportunity.Owner_Details__c);
    System.assertEquals(opportunity.SalesManager__c, Opportunity.Owner_Details__r.ManagerID);

//Test that the Owner Details changes with an Opp Owner Change
   
        opportunity.OwnerId = user2.ID;
        update opportunity;
     test.stopTest();

//Validate that the Owner Details and Manager fields were updated.
    opportunity = [SELECT OwnerID, Owner_Details__c, Owner_Details__r.ManagerID, SalesManager__c FROM Opportunity WHERE id = :opportunity.id];
    System.assertEquals(opportunity.Owner_Details__c, user2.Id);
    System.assertEquals(opportunity.SalesManager__c, user2.Manager.Id);
    }
}

 Thank you for the help!

 

Regular Contributor
Andrew Wilkinson
Posts: 121
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

I see a few issues with your trigger. Can you post the entire code so this can be resolved?

Andrew Wilkinson
Certified Salesforce Developer
Check out my blog. http://andrewwilkinsonsf.blogspot.com/
Regular Contributor
Andrew Wilkinson
Posts: 121
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

But a few things that need to be resolved are recursion(which could be satsfied without adding an additional update to this field. The portion displayed in this trigger should occur before update and not after update since it is the same object that is being updated. This would solve the recursion. Also, I would like to see if there are other ways to optimize this trigger. I noticed you are running DML's on individual statements instead of in bulk. This can cause issues and failures as well. Please post your code and I can resolve these for you and give you some tips.

Andrew Wilkinson
Certified Salesforce Developer
Check out my blog. http://andrewwilkinsonsf.blogspot.com/
Regular Contributor
mgodsey
Posts: 85
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

Hi Andrew - thanks so much for your help! I'm new to Apex and have never done any coding before, so I really appreciate all of the tips I can get.

 

Here is the entire trigger:

 

trigger CopyOwnerInformation on Opportunity (before Insert, after Update) {

//------------Before Insert----------------------
//Copy Owner to Owner Details Field

if(Trigger.isBefore && Trigger.isInsert){
for (Opportunity x: Trigger.New) {x.Owner_Details__c=x.OwnerID;
}
}

//-------------After Update----------------------------------

//compare old and new record to see if the Sales Manager field has changed
if(Trigger.isAfter && Trigger.isUpdate){

//create map to compare old Opp values
for (Opportunity opp: trigger.new) {
    Opportunity oldopp = Trigger.oldMap.get(opp.ID);

//Insert new owner details ID if it has changed
if(opp.Owner_Details__c == null || (opp.OwnerId != oldopp.OwnerID)){
List<Opportunity> OppsWithNewOwner = [SELECT ID, OwnerID, Owner_Details__c FROM Opportunity WHERE id in:trigger.new];

for (Integer i=0; i<OppsWithNewOwner.size(); i++){                                      
    OppswithNewOwner[i].Owner_Details__c = OppsWithNewOwner[i].OwnerID;}
    
    update OppsWithNewOwner;

    }

//Update Sales Manager Field    
if(opp.SalesManager__c == null || (opp.SalesManagerID__c != oldopp.SalesManagerID__c)){
List<Opportunity> UpdateOpps = [SELECT Id, SalesManager__c, SalesManagerID__c
                                FROM Opportunity
                                WHERE Id in: trigger.new];
// for records that require an update, copy Sales Manager ID to Sales Manager field                                
    for (Integer i=0; i<UpdateOpps.size(); i++){
        UpdateOpps[i].SalesManager__c = UpdateOpps[i].SalesManagerID__c;
        }                             
    Update UpdateOpps;
}
}
} // end of after update
} // end of trigger

 

Regular Contributor
mgodsey
Posts: 85
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

I'm sure there is a ton to fix in the trigger, but I should also mention that I haven't run into the max trigger depth error when making changes in the UI. It's only occuring in my test method.

 

Thanks again! 

Regular Contributor
Andrew Wilkinson
Posts: 121
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

[ Edited ]

Made a few changes. I commented out the after code and turned it into a before code.

 

trigger CopyOwnerInformation on Opportunity (before update, before Insert, after Update) {

//------------Before Insert----------------------
//Copy Owner to Owner Details Field

if(Trigger.isBefore && Trigger.isInsert){
for (Opportunity x: Trigger.New) {x.Owner_Details__c=x.OwnerID;
}
}

//-------------Before Update---------------------------------

if(Trigger.isbefore && Trigger.isUpdate){
    for(Opportunity o : Trigger.new){
        if(o.Owner_Details__c == null || (o.OwnerId != Trigger.oldMap.get(o.Id).OwnerId)){
            o.Owner_Details__c = o.OwnerId;   
        }
        if(o.SalesManager__c == null || (o.SalesManagerID__c != Trigger.oldMap.get(o.Id).SalesManagerID__c)){
            o.SalesManager__c = o.SalesManagerID__c;   
        }
    }

}

//-------------After Update----------------------------------

//compare old and new record to see if the Sales Manager field has changed
/*if(Trigger.isAfter && Trigger.isUpdate){

//create map to compare old Opp values
for (Opportunity opp: trigger.new) {
    Opportunity oldopp = Trigger.oldMap.get(opp.ID);

//Insert new owner details ID if it has changed
if(opp.Owner_Details__c == null || (opp.OwnerId != oldopp.OwnerID)){
List<Opportunity> OppsWithNewOwner = [SELECT ID, OwnerID, Owner_Details__c FROM Opportunity WHERE id in:trigger.new];

for (Integer i=0; i<OppsWithNewOwner.size(); i++){                                      
    OppswithNewOwner[i].Owner_Details__c = OppsWithNewOwner[i].OwnerID;}
    
    update OppsWithNewOwner;

    }

//Update Sales Manager Field    
if(opp.SalesManager__c == null || (opp.SalesManagerID__c != oldopp.SalesManagerID__c)){
List<Opportunity> UpdateOpps = [SELECT Id, SalesManager__c, SalesManagerID__c
                                FROM Opportunity
                                WHERE Id in: trigger.new];
// for records that require an update, copy Sales Manager ID to Sales Manager field                                
    for (Integer i=0; i<UpdateOpps.size(); i++){
        UpdateOpps[i].SalesManager__c = UpdateOpps[i].SalesManagerID__c;
        }                             
    Update UpdateOpps;
}
}
}*/ // end of after update
} // end of trigger

 This can all be done with a before update. But I am pretty sure you don't even need the trigger and this should be done in a workflow rule.

Andrew Wilkinson
Certified Salesforce Developer
Check out my blog. http://andrewwilkinsonsf.blogspot.com/
Regular Contributor
mgodsey
Posts: 85
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

Hi Andrew - thank you again for your help! I'll test this now and let you know how it goes and after mark it as a solution.

 

The reason this wouldn't work with workflow rules was that the SalesManager__c and Owner_Details__c fields are user lookup fields, and field updates with look up fields are pretty restrictive. You can just choose a specific person, not update it with an ID.

Regular Contributor
mgodsey
Posts: 85
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

I'm running into a bit of trouble. The SalesManagerID__c field is a formula that uses the Owner_Details__c field to get the user's manager's ID. However, with the way the trigger is set up, the Sales Manager ID that is being grabbed to update SalesManager__c is the value from before the trigger firing. It's updating to match new Owner_Details__c field.

 

I suppose I could create a Sales Manager ID field that is not a formula and update that in the trigger as well, but is there a way that does not involve making a new field?

 

I tried the following, but now it's not updating the Sales Manager field at all.

 

if(Trigger.isbefore && Trigger.isUpdate){
    for(Opportunity o : Trigger.new){
        if (o.Owner_Details__c == null || (o.OwnerID != Trigger.oldMap.get(o.ID).OwnerID)){
            o.Owner_Details__c = o.OwnerId;
            
        if (o.SalesManager__c == null || (o.SalesManagerID__c != Trigger.oldMap.get(o.ID).SalesManagerID__c)){
            o.SalesManager__c = Trigger.newMap.get(o.ID).SalesManagerId__c;
            }
         
         }
    }
}

 

Regular Contributor
Andrew Wilkinson
Posts: 121
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

[ Edited ]

Formula fields are tricky. So you would need to replicate that field formula in the trigger

Andrew Wilkinson
Certified Salesforce Developer
Check out my blog. http://andrewwilkinsonsf.blogspot.com/
Regular Contributor
mgodsey
Posts: 85
0

Re: Unit Test Failing Because of Maximum Trigger Depth Exceeding

This is what the original formula was: Owner_Details__r.ManagerId

 

Here is what I did to try to replicate this in the trigger:

 

trigger CopyOwnerInformation on Opportunity (before Insert, before update) {

//------------Before Insert----------------------
//Copy Owner to Owner Details Field

if(Trigger.isBefore && Trigger.isInsert){
for (Opportunity x: Trigger.New) {
    x.Owner_Details__c=x.OwnerID;
}
}

//----------------Before Update------------------------------

if(Trigger.isbefore && Trigger.isUpdate){
    for(Opportunity o : Trigger.new){
            o.Owner_Details__c = o.OwnerId;
                system.debug('********* Owner Details ID**********' + o.Owner_Details__c);
            o.SalesManagerIDv2__c = o.Owner_Details__r.ManagerID;
                system.debug('********* Owner Details ID + Sales Manager Id **********' + o.Owner_Details__c+ '    '  
+ o.SalesManagerIDv2__c); o.SalesManager__c = o.SalesManagerIDv2__c;

 

The debug log shows that Owner Details updates properly, but the SalesManagerIDv2 field is null (even though the user has a manager  on their user record). Any idea what I'm doing wrong?

 

Am I better of just making this an "After Update" trigger again? I understand the reasoning for avoiding that, but it was working (with the exception of the Unit Test)