You need to sign in to do that
Don't have an account?
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!
In one of my previous posts I stated that this could be done with a workflow rule. I still recommend that option as a best practice. However if you wanted to pursue this for learning purposes or whatever reason you would need to query the records, populate it into a map, and call the values from the map.
All Answers
I see a few issues with your trigger. Can you post the entire code so this can be resolved?
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.
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:
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!
Made a few changes. I commented out the after code and turned it into a before code.
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.
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.
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.
Formula fields are tricky. So you would need to replicate that field formula in the trigger
This is what the original formula was: Owner_Details__r.ManagerId
Here is what I did to try to replicate this in the trigger:
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)
In one of my previous posts I stated that this could be done with a workflow rule. I still recommend that option as a best practice. However if you wanted to pursue this for learning purposes or whatever reason you would need to query the records, populate it into a map, and call the values from the map.
Thank you so much for your help! I just had to switch out one thing but the map really did the trick. I appreciate you taking so much time to help me get to the solution!
And I agree that using workflow before triggers is defintely the way to go. I tried using workflow for this, but I don't see anyway to create a field update that updates a user lookup field based on an ID. When creating the field update it makes you choose a specific user. But if anyone has figured out a way to do this, I would love to know!