0

I'm hoping I am missing something obvious.

I have a Lead Trigger & Handler class. The Handler forces Assignment Rules to fire if a Lead Checkbox field IS CHANGED to True. After Update. It is working for users.

The problem is my Handler test class coverage stops right at the line where I am checking whether the field "Attention Required" has been changed to true:

  public class LeadTriggerHandler extends TriggerHandlerFactory{
  private Map<Id, Lead> newLeadMap;
  private Map<Id, Lead> oldLeadMap;

  public LeadTriggerHandler() {
    this.newLeadMap = (Map<Id, Lead>) Trigger.newMap;
    this.oldLeadMap = (Map<Id, Lead>) Trigger.oldMap;
  }

    public override void afterUpdate() {
        try{
        attentionRequiredLeadAssignment();
        }catch(Exception e){
            Utils.CreateSystemException(e);
        }
    }   

  /*
  * If a Lead is set to "Attention Required"
  * and is owned by one of the following Queues: Recycle, Nurture, Junk
  * then trigger Assignment Rules to reassign Lead
  */
  private void attentionRequiredLeadAssignment() {
    List<Lead> leads = new List<Lead>();
    List<String> queueNameList = new List<String>();
    Set<String> queueNameSet = new Set<String>();

    for(Lead l : [SELECT Id,attention_required__c,Owner.Name FROM Lead WHERE Id IN :newLeadMap.keyset()]){
      //if attention required is changed to TRUE
      if(newLeadMap.get(l.id).attention_required__c != oldLeadMap.get(l.id).attention_required__c && newLeadMap.get(l.id).attention_required__c == true){
        //getting Queue names from custom setting
        LeadTriggerHandlerSetting__c setting = LeadTriggerHandlerSetting__c.getValues('AttentionRequired');
        queueNameList = setting.queue_names__c.split(','); 
        queueNameSet.addAll(queueNameList);
        //and lead owner is recycle, nurture, or junk leads
        if(queueNameSet.contains(l.Owner.Name)){
          leads.add(l);
        }
      }
    }

    if(!leads.isEmpty()) {
       Database.DMLOptions dmo = new Database.DMLOptions();
       //force assignment rules to fire
       dmo.assignmentRuleHeader.useDefaultRule = true;
       Database.SaveResult[] lsr = Database.update(leads, dmo);

       Integer i=0;
       for(Database.SaveResult sr : lsr) {
          if(!sr.isSuccess()){
              leads[i].addError('Lead Assignment Error' + sr.getErrors()[0].getMessage());
          }
          i++;
       } 
    }
  }


}

It seems obvious enough that in the Test Class I need to insert a Lead with "Attention Required" set to false, and then update it to true. However this is not getting me coverage past the line:

if(newLeadMap.get(l.id).attention_required__c != oldLeadMap.get(l.id).attention_required__c && newLeadMap.get(l.id).attention_required__c == true){

This is the Test Class:

@isTest
private class LeadTriggerHandlerTest
{
    @isTest
    static void testLeadAssignment() {
        Group testGroup = new Group(Name='Junk Leads', Type='Queue');
        insert testGroup;

        System.runAs(new User(Id=UserInfo.getUserId()))
        {
            QueuesObject testQueue = new QueueSObject(QueueID = testGroup.id, SObjectType = 'Lead');
            insert testQueue;
        }

        LeadTriggerHandlerSetting__c setting = new LeadTriggerHandlerSetting__c();
        setting.Name = 'AttentionRequired';
        setting.Queue_Names__c = 'Junk Leads,Nurture Pool';
        insert setting;

        DataFactory factory = new DataFactory();

        Lead l = factory.createLead();
        l.OwnerId = testGroup.id;
        l.Attention_Required__c = false;
        upsert l;

        l.Attention_Required__c = true;
        update l;
    }
}

Any ideas?

1
  • sorry about the delay one thing i was thinking is why are you looping via a soql, when you can loop on newLeadMap.getValues(). I have a feeling the record is not in the execution context which is why it cannot see the record in the SOQL query. Not sure if this is the cause but this is as far i could think right now Vish
    – user26301
    Commented Nov 12, 2015 at 19:05

3 Answers 3

1

I've seen similar issues when you have recursive trigger control logic implemented in your trigger pattern.

  1. When executed via the UI, it works fine because there are two transactions
  2. When executed in the testmethod, the second update does nothing because it is in the same transaction.

Clear out the static variable used to control recursive triggers between the upsert and update to simulate the user's UI experience

4
  • This turned out to be a bit of red herring, as there were other triggers outside of the trigger framework causing recursion problems. I was using the ".bypass" feature of the framework (referenced in the comment below) to avoid recursion but in doing do was skipping the method altogether. I actually had 2 private methods being called After Update, and 1 one them was causing another trigger to fire in recursion. I simply included 1 private method to make the post easier to digest. The ultimate solution was to reverse the order in which I was calling the methods in the try/catch Commented Nov 14, 2015 at 9:20
  • CONT - but this is likely only a solution to my own problem, as there were a number of independent triggers on Lead that I was unaware of. Regardless, crop1645's recommendation is a good one, even though I'm not sure that point #2 is valid since the test method should not be executing differently that the UI. Then again Salesforce can be weird. Commented Nov 14, 2015 at 9:28
  • Closing this issue, as the solution was unrelated to the title and may be misleading to folks who stumble across this. Commented Nov 14, 2015 at 9:29
  • Matt -- I agree testmethod shouldn't execute differently than UI -- but testmethod involved 3 DML operation in the same transaction - create lead, update lead to get desired initial state, then upsert lead. In the UI, this would be 3 separate transactions. If you have trigger control in your code via static variables, then those static variables will not be reset in the testmethod execution but will be reset in the UI as a natural consequence of each SAVE action. Hence testmethods need to flush those static variables between simulated UI DML events. Regardless, I'm glad you found the problem
    – cropredy
    Commented Nov 17, 2015 at 0:44
0

Looking at the code it isn't clear how you are setting the values in your constructor, as you are not passing any parameters

public LeadTriggerHandler() {

    this.newLeadMap = (Map<Id, Lead>) Trigger.newMap;
    this.oldLeadMap = (Map<Id, Lead>) Trigger.oldMap;
  }

Your problem might be coming from here as this.newLeadMap and oldLeadMap might not even have any values

Regards Vishant

3
  • Hi Vishant - I am casting the values from a LeadTrigger to the Handler class via a Trigger Framework utility: github.com/kevinohara80/sfdc-trigger-framework </br> Commented Nov 12, 2015 at 16:49
  • According to my debug logs there are values being passed to the private method from both newLeadMap and oldLeadMap. Commented Nov 12, 2015 at 16:59
  • Also the trigger is working - Assignment Rules are firing correctly after the Lead.Attention_Required__c checkbox is changed to True. Commented Nov 12, 2015 at 17:13
0

This turned out to be a bit of red herring, as there were other triggers outside of the trigger framework causing recursion problems.

I was using the ".bypass" feature of the framework (referenced in the comment below) to avoid recursion but in doing do was skipping the method altogether. I actually had 2 private methods being called After Update, and 1 one them was causing another trigger to fire in recursion. I simply included 1 private method to make the post easier to digest.

The ultimate solution was to reverse the order in which I was calling the methods in the try/catch but this is likely only a solution to my own problem, as there were a number of independent triggers on Lead that I was unaware of.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .