Find your content:

Search form

You are here

Help reaching 100% coverage

 
Share

I have a relatively simple class to add contact roles to an Opportunity after conversion. However, I cannot reach 100% code coverage and I really can't figure it out.

Here's my code:

Trigger:

trigger Opportunities on Opportunity (after insert) {

    Opportunities o = new Opportunities();
    o.SetContactRoleDefaults(Trigger.new);     
}

Class:

public class Opportunities {

    public Opportunities()
    {
    }

    public void SetContactRoleDefaults(Opportunity[] opptys) 
    {
        set<ID> set_opptyIDs = new set<ID>();

        for(Opportunity o:opptys) { 
            set_opptyIDs.add(o.id);
        }

        list<OpportunityContactRole> list_opptyContactRolesToUpdate = new list<OpportunityContactRole>();
        for(OpportunityContactRole ocr:[select Id,IsPrimary,Role from OpportunityContactRole where OpportunityId in :set_opptyIDs]) { 
            **ocr.IsPrimary = true;
            ocr.Role = 'Decision Maker';
            list_opptyContactRolesToUpdate.add(ocr);**
        }

        if (list_opptyContactRolesToUpdate.size() > 0) {
            **update list_opptyContactRolesToUpdate;**
        }

    }
}

Test class:

@isTest
private class Opportunities_Test {

    static testMethod void SetContactRoleDefaults_Test() {

        Lead myLead = new Lead(FirstName = 'Fred', LastName = 'Fry', Company='Fry And Sons');
        insert myLead;

        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(myLead.id);

        LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];
        lc.setConvertedStatus(convertStatus.MasterLabel);

        Database.LeadConvertResult lcr = Database.convertLead(lc);
        System.assert(lcr.isSuccess());

        List<Opportunity> opps = new List<Opportunity>([
            SELECT Name, Id
            FROM Opportunity
            WHERE Name = 'Fry And Sons'
        ]);

        for (Opportunity o :opps)
            system.assertEquals('Fry And Sons', o.Name);

        List<Contact> conts = new List<Contact>([
            SELECT Id, Name
            FROM Contact
            WHERE Name = 'Fred Fry'
        ]);

        List<OpportunityContactRole> oppContacts = new List<OpportunityContactRole>([
            SELECT ContactId, OpportunityId, Role, IsPrimary
            FROM OpportunityContactRole
            WHERE ContactId IN :conts
            AND OpportunityId IN :opps
        ]);  

        for (OpportunityContactRole ocr :oppContacts) {
            system.assertEquals('Decision Maker', ocr.Role);
            system.assertEquals(true, ocr.IsPrimary);
        }        
    }     
}

Any ideas?


Attribution to: Davin Casey

Possible Suggestion/Solution #1

This is an order of execution issue I believe, though I'm really struggling to find any documentation on exactly how this works.

The docs for the lead conversion additional settings state that if triggers are enabled for lead convert and the opportunity has before triggers, these fire before the opportunity contact roles are created, though there is no information about what happens to the after triggers.

I've managed to get 100% code coverage by moving the contact role update code into an @future method - this runs after the lead convert transaction has completed so the role object are guaranteed to be inserted:

public class Opportunities {

public Opportunities()
{
}

public void SetContactRoleDefaults(Opportunity[] opptys) 
{
    set<ID> set_opptyIDs = new set<ID>();

    for(Opportunity o:opptys) { 
        set_opptyIDs.add(o.id);
    }

    AsynchContactRoleDefaults(set_opptyIds);
}

@future
public static void AsynchContactRoleDefaults(Set<Id> set_opptyIDS)
{

    list<OpportunityContactRole> list_opptyContactRolesToUpdate = new list<OpportunityContactRole>();
    for(OpportunityContactRole ocr:[select Id,IsPrimary,Role from OpportunityContactRole where OpportunityId in :set_opptyIDs]) { 
        ocr.IsPrimary = true;
        ocr.Role = 'Decision Maker';
        list_opptyContactRolesToUpdate.add(ocr);
    }

    if (list_opptyContactRolesToUpdate.size() > 0) {
        update list_opptyContactRolesToUpdate;
    }

}
}

The other change I've made is to wrap the convert in start/stoptest calls - this forces the @future method to fire in the test context:

@isTest
private class Opportunities_Test {

    static testMethod void SetContactRoleDefaults_Test() {

        Lead myLead = new Lead(FirstName = 'Fred', LastName = 'Fry', Company='Fry And Sons');
        insert myLead;

        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(myLead.id);

        LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];
        lc.setConvertedStatus(convertStatus.MasterLabel);

        Test.startTest();
        Database.LeadConvertResult lcr = Database.convertLead(lc);
        System.assert(lcr.isSuccess());
        Test.stopTest();

        List<Opportunity> opps = new List<Opportunity>([
            SELECT Name, Id
            FROM Opportunity
            WHERE Name = 'Fry And Sons'
        ]);

        for (Opportunity o :opps)
            system.assertEquals('Fry And Sons', o.Name);

        List<Contact> conts = new List<Contact>([
            SELECT Id, Name
            FROM Contact
            WHERE Name = 'Fred Fry'
        ]);

        List<OpportunityContactRole> oppContacts = new List<OpportunityContactRole>([
            SELECT ContactId, OpportunityId, Role, IsPrimary
            FROM OpportunityContactRole
            WHERE ContactId IN :conts
            AND OpportunityId IN :opps
        ]);  

        for (OpportunityContactRole ocr :oppContacts) {
            system.assertEquals('Decision Maker', ocr.Role);
            system.assertEquals(true, ocr.IsPrimary);
        }        
    }     
}

Attribution to: Bob Buzzard

Possible Suggestion/Solution #2

If you check the "Enable Validation and Triggers from Lead Convert", it fires Before Triggers and Validations on Lead Conversion. I have it on record from Salesforce Support that the After triggers fire all the time, irrespective of this setting being checked.

You could try moving the logic from your OpportunityAfter trigger to your LeadAfter trigger, and invoke the Opportunities.SetContactRoleDefaults in your LeadAfter trigger, when you can detect a converted Lead (lead.convertedOpportunityId will be set after conversion), and then query for the OpportunityContactRoles from that context, you should be able to get to the OCR's

(As a footnote, if you're not getting 100% coverage, it could just be a symptom of an underlying problem, where the code is not quite doing what you want it to do. Is it actually setting the OCR's created to Decision Maker and IsPrimary = true when you use it outside of a test ?)

References : http://alexdberg.blogspot.co.uk/2011/03/convertlead-lead-conversion-process-of.html http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_order_of_execution.htm

 trigger LeadAfter on Lead(After Update){
    List<OpportunityContactRole> oppContacts = new List<OpportunityContactRole>{};

    for(Lead ld : trigger.new){

    if(ld.isConverted && !trigger.oldMap.get(ld.Id).IsConverted){
    Opportunity opp = [Select Id, Name, (Select Id, Role from OpportunityContactRoles) from Opportunity where Id = :ld.ConvertedOpportunityId];

    for(OpportunityContactRole ocr : opp.OpportunityContactRoles){
    ocr.IsPrimary = true;
    ocr.Role = 'Decision Maker';
    oppContacts.add(ocr);
}
    }

    }

    if(!oppContacts.isEmpty())
    update oppContacts ; 
    }

Attribution to: techtrekker
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/3852

My Block Status

My Block Content