0

I have an after insert method on OpportunityLineItem that I'm writing a test class for. A problem that I had is I couldn't get the test to assert on the list passed to it because a formula was null when I expected it to be populated.

I thought to myself if I just reproduce the formula into the trigger logic then I can get around the issue but in doing so I've stopped the logic from working entirely where it worked in the UI before.

Consider this example:

I have a formula field "Product Family" that has the following syntax:

Text(PricebookEntry.Product2.Family)

When I put it into this method it works when you create a new OpportunityLineItem record in the UI, the solution consultant field is populated:

public static void populateSolutionsConsultant(List<OpportunityLineItem> newList)
{
    List<OpportunityTeamMember> teamMembers = new List<OpportunityTeamMember>();
    Map<Id,List<OpportunityLineItem>> oppToLineItem = new Map<Id,List<OpportunityLineItem>>();
    for(OpportunityLineItem oli:newList)
    {
        if(oli.Product_Family__c == 'Analytic Solutions')
        {
            if(oppToLineItem.get(oli.OpportunityId)==null)
            {
                List<OpportunityLineItem> oliLst = new List<OpportunityLineItem>();
                oliLst.add(oli);
                oppToLineItem.put(oli.OpportunityId,oliLst);
            }
            else
            {
                oppToLineItem.get(oli.OpportunityId).add(oli);
            }
        }
    }
    teamMembers = [SELECT UserID,OpportunityId FROM OpportunityTeamMember where OpportunityId in:oppToLineItem.keyset() AND TeamMemberRole='Solutions Consultant'];
    for(OpportunityTeamMember oppTeamMemb:teamMembers)
    {
        List<OpportunityLineItem> olis = oppToLineItem.get(oppTeamMemb.OpportunityId);
        for(OpportunityLineItem line:olis)
        {
            line.Solution_Consultant__c = oppTeamMemb.UserID;
        }
    }
}

However, because I can't run my test on the formula I edited line looking for Analytic Solutions to be what the formula is, cutting the formula field out of the logic. Like this the solution consultant is not populated when created in the UI:

public static void populateSolutionsConsultant(List<OpportunityLineItem> newList)
{
    List<OpportunityTeamMember> teamMembers = new List<OpportunityTeamMember>();
    Map<Id,List<OpportunityLineItem>> oppToLineItem = new Map<Id,List<OpportunityLineItem>>();
    for(OpportunityLineItem oli:newList)
    {
        if(oli.PricebookEntry.Product2.Family == 'Analytic Solutions')
        {
            if(oppToLineItem.get(oli.OpportunityId)==null)
            {
                List<OpportunityLineItem> oliLst = new List<OpportunityLineItem>();
                oliLst.add(oli);
                oppToLineItem.put(oli.OpportunityId,oliLst);
            }
            else
            {
                oppToLineItem.get(oli.OpportunityId).add(oli);
            }
        }
    }
    teamMembers = [SELECT UserID,OpportunityId FROM OpportunityTeamMember where OpportunityId in:oppToLineItem.keyset() AND TeamMemberRole='Solutions Consultant'];
    for(OpportunityTeamMember oppTeamMemb:teamMembers)
    {
        List<OpportunityLineItem> olis = oppToLineItem.get(oppTeamMemb.OpportunityId);
        for(OpportunityLineItem line:olis)
        {
            line.Solution_Consultant__c = oppTeamMemb.UserID;
        }
    }
}

Why can't I use "PricebookEntry.Product2.Family" in the if statement?

EDIT:

For reference this is the test class that I wrote that doesn't asset.. yet.

@isTest static void testSolutionConsultant()
{
    List<OpportunityLineItem> lst = new List<OpportunityLineItem>();
    Opportunity o = [SELECT Id FROM Opportunity];
    PricebookEntry pbEntry = [SELECT Id FROM PricebookEntry];
    Product2 prod = [SELECT Id, Family FROM Product2];
    User u = [SELECT Id FROM User WHERE UserName = '[email protected]'];
    Id tempId = getDummyID();

    OpportunityTeamMember oTeam = new OpportunityTeamMember();
        oTeam.UserID = u.Id;
        oTeam.OpportunityId = o.Id;
        oTeam.TeamMemberRole = 'Solutions Consultant';
        oTeam.OpportunityAccessLevel = 'Read';
    insert oTeam;

    lst.add(new OpportunityLineItem(
        Id = tempId,
        OpportunityId = o.Id,
        PricebookEntryId = pbEntry.Id,
        Solution_Consultant__c = null
    ));

    Test.startTest();
        SL_OpportunityLineItemTriggerHandler.populateSolutionsConsultant(lst);
    Test.stopTest();

    System.assertEquals(u.Id, lst[0].Solution_Consultant__c,
        'Solution Consultant should be populated');
}
2
  • So...you're still testing using SeeAllData? That makes it a lot harder to help you.
    – Adrian Larson
    Commented Sep 28, 2017 at 15:36
  • No? I have @testsetup it just was a ton of lines that didn't seem useful., Because the main goal of this question was to identify why not using the formula field stopped the logic from working. The test class is the end goal yes, but if I can't get it to even work in regular Salesforce then I know the test won't work. Commented Sep 28, 2017 at 15:38

1 Answer 1

2
  1. You should have no problem with using the formula field during a test method as long as you set up the data appropriately

  2. The reason oli.PricebookEntry.Product2.Family does not work is that related fields are not populated as part of the trigger records or the records you seem to have set up. You will need to explicitly query for them.

So your real question is what is going on with your tests when using the formula field. To answer that you would have to redo the entire question. Better yet post a new questions since this one has now been answered. When you do post a new question include your test code including the data set up as that will determine if your formula during tests will be populated correctly

In your current test you are doing

lst.add(new OpportunityLineItem(
        Id = tempId,
        OpportunityId = o.Id,
        PricebookEntryId = pbEntry.Id,
        Solution_Consultant__c = null
    ));

You are then passing that into your method. Problem is that since the record has not been inserted the formula is not present and not updated so you should be executing the trigger by inserting it or updating it depending on the use case and have it run the method then fields will be populated.

You cannot use the recalculateFormulas method as

This method doesn’t recalculate cross-object formulas. If you call this method on objects that have both cross-object and non-cross-object formula fields, only the non-cross-object formula fields are recalculated.

So you will need to indirectly test your class by causing the trigger that executes the class to fire with the appropriate values.

2
  • This is solid advice. Commented Sep 28, 2017 at 18:27
  • and also be aware of this limitation - not sure if fixed yet
    – cropredy
    Commented Sep 29, 2017 at 1:28

You must log in to answer this question.

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