+ Start a Discussion
Zeeshan HussainZeeshan Hussain 

Problem with testing future method in Salesforce

Hi guys,

I'll give you a little background information of what I am trying to achieve and maybe some experienced developers here could help to explain the error of my ways.

Aim of the future class
I have a 'Contract' object and a 'Contract Participant' object that is related to the contract. 
When a field called 'Contract_To__c' (The contract end date) is changed on the contract, the future class is called by a trigger and then the contract participant end date on the contract participant is updated automatically to the same value to reflect the change if the contract end date was originally the same as the contract participant end date

What I am doing
I have a test class set up with test data. And then within the Test.StartTest() and StopTest() methods, I call the future method, passing in the parameter values after updating the contract end date. After the stopTest() method, I am logging the participant end date to see if the future class has updated this to reflect the contract end date. This does not happen.

Below is the code for my test class and the class for my future method.

Test class:
@isTest
public class TestClassZee {

    public static testMethod void test_ContractParticipant(){
        
       Date sys_lastYearStart = Date.newInstance(Date.Today().Year()-1,1,1);
       Date sys_nextYearEnd = Date.newInstance(Date.Today().Year()+1,12,31);
        
         //Dynamically obtain the first value from the global division picklist
           Schema.DescribeFieldResult fieldResult = Portfolio__c.Team__c.getDescribe();
           List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
           String firstValue = ple[0].getLabel(); 
        
        //Insert a workstream
        List<Portfolio__c> wsToInsert = UtilityClass.getWorkstreams(1, 'Agreement', firstValue);
        insert wsToInsert;
        
        //Insert a supplier
        List<Account> supplierToInsert = UtilityClass.getSuppliers(2);
        insert supplierToInsert;
        
        //Create a contract
        List<HTE_Contract__c> conToInsert = UtilityClass.getContracts(2, 'valid', supplierToInsert[0].Id, wsToInsert[0].Id);
        insert conToInsert;
        
        //Create an opportunity
        List<Opportunity> oppToInsert = UtilityClass.getOpportunities(2, supplierToInsert[0].Id, wsToInsert[0].Id,    'Discussion Required');
        insert oppToInsert;
        
        //Create a contract participant
        List<Contract_Participant__c> cpToInsert = UtilityClass.getParticipants(1, conToInsert[0].Id, supplierToInsert[0].Id, oppToInsert[0].Id, sys_lastYearStart, sys_nextYearEnd);
        insert cpToInsert;

        /*System.debug(LoggingLevel.INFO, 'Checking workstream id ' + wsToInsert[0].Id);
        System.debug(LoggingLevel.INFO, 'Checking account id ' + supplierToInsert[0].Id);
        System.debug(LoggingLevel.INFO, 'Checking opp id ' + oppToInsert[0].Id);
        System.debug(LoggingLevel.INFO, 'Checking contract id ' + conToInsert[0].Id);
        System.debug(LoggingLevel.INFO, 'Checking contract participant id ' + cpToInsert[0].Id);*/
        
        Test.startTest();
        //Change the to date
        Date oldDate = conToInsert[0].Contract_To__c;
        
        conToInsert[0].Contract_To__c = Date.valueof('2020-01-01');
        conToInsert[0].Latest_Modification__c = 'Changing contract To date';
        update conToInsert;
        
        Date newDate = conToInsert[0].Contract_To__c;
        
        System.assertEquals(Date.valueof('2020-01-01'), conToInsert[0].Contract_To__c);
        
        //Call the future class
        FutureCPDateUpdate.updateParticipantEndDates(conToInsert[0].Id, oldDate, newDate);
        
        Test.stopTest();
        
        System.debug(LoggingLevel.INFO, 'Logging cp end date ' + cpToInsert[0].Participant_To__c);
        
    }
}

Future Method/Class
public class FutureCPDateUpdate {
    
	@future(callout = true)
	public static void updateParticipantEndDates(Id contractId, Date oldDate, Date newDate) {

		List<Contract_Participant__c> cp = [SELECT Id, Participant_To__c, Participant_From__c FROM Contract_Participant__c WHERE HTE_Contract__c =:contractId ];
		
        for(Contract_Participant__c cp2 : cp) {
            if(cp2.Participant_To__c == oldDate) {
                cp2.Participant_To__c = newDate;     
            }
        }
        
        update cp;
	}
    
}
When I make this change myself without running the code, I have a trigger which calls the future method and it updates the participant end dates just fine. In the future method, it performs a SOQL query for a Contract Participant record. Could it be because It cannot query for test data? I don't see a way around this. I appreciate that I am new to this.

Sorry if this was too much information or if anything was not clearly explained, I am more than happy to explain things further.

Thank you for your time. 
Best Answer chosen by Zeeshan Hussain
Gururaj BGururaj B
Hi
I see couple of issues here.
1. You have already calling the future class from trigger. So when you update the Contract in your test class the Trigger and so the future class will be called there itself. So no need to call explicitly in your test class.
2. future classes are run asynchronosly, that is the execution of the future class will be internaly in the queue and server picks it up whenever its free. As you are updating the contract participant object in this future class the current test class instance will not have any access to updated values. In your debug statement "cpToInsert[0].Participant_To__c" this will always return the old value that is set in the test class instance. To get the updated vales you need to requery using select statment. But even that might not work as the future class might not have executed by the time your select statment is executing. Provide some sleep time before your select statment(may be like 10-20 seconds).

Please mark as best answer if it gave some information for you..

All Answers

Gururaj BGururaj B
Hi
I see couple of issues here.
1. You have already calling the future class from trigger. So when you update the Contract in your test class the Trigger and so the future class will be called there itself. So no need to call explicitly in your test class.
2. future classes are run asynchronosly, that is the execution of the future class will be internaly in the queue and server picks it up whenever its free. As you are updating the contract participant object in this future class the current test class instance will not have any access to updated values. In your debug statement "cpToInsert[0].Participant_To__c" this will always return the old value that is set in the test class instance. To get the updated vales you need to requery using select statment. But even that might not work as the future class might not have executed by the time your select statment is executing. Provide some sleep time before your select statment(may be like 10-20 seconds).

Please mark as best answer if it gave some information for you..
This was selected as the best answer
Zeeshan HussainZeeshan Hussain
Thank you Gururaj, I added in a delay (I overlooked the fact that the future call was an async method) and I modified my code to query the object separately and that worked. Many thanks for your input, it was greatly appreciated!