3

Ok, I'm officially confused. I've written some code to attempt to populate a Campaign lookup field on Opportunity based on some logic. The basic flow is Process Builder (so the client can declaratively change when the logic fires if desired) > Invocable Method > Queueable.

My code works just fine in the context of my unit tests, but when I try to test it in sandbox I get a null-pointer exception.

Invocable:

public class PrimaryCampaign {

    @InvocableMethod
    public static void setPrimaryCampaign(List<Id> oppIds){
        System.debug(oppIds); //populated
        System.enqueueJob(new PrimaryCampaignQueueable(oppIds));
    }

}

Simplified Queueable:

public class PrimaryCampaignQueueable implements Queueable{

    public static List<Id> oppList;

    public PrimaryCampaignQueueable(List<Id> oppIds){
        oppList = oppIds;
    }

    public static void execute(QueueableContext qc){
        System.debug(oppList); //NPE occurs here, but when I run unit tests, this is populated
        //do logic
    }

}

Simplified Test Class:

@isTest
public class TestPrimaryCampaign {

    @testSetup
    static void setUp(){

        //set up data including Accounts, Contacts, Campaigns, CampaignMembers, Opportunities & OpportunityContactRoles
    }

    @isTest
    static void testPrimaryCampaign(){
        List<Id> oppIds = new List<Id>();
        Opportunity o = [SELECT Id FROM Opportunity];
        oppIds.add(o.Id);
        Test.startTest();
        PrimaryCampaign.setPrimaryCampaign(oppIds);
        Test.stopTest();

        //do assertions
    }

}

I've tried testing both through the UI to trigger the Process Builder, as well as just running the following anonymous block in the dev console getting the same NPE both ways:

List<Id> oppIds = new List<Id>();
oppIds.add('0062F000002USgZQAW');
PrimaryCampaign.setPrimaryCampaign(oppIds);

I also tried making the oppList not static and defining it with:

this.oppList = oppIds;

But that fails to compile with an error that the variable oppList doesn't exist. I am so confused. What is going on here?

1 Answer 1

5

Your oppList shouldn't be static.

It should be a member variable.

Try this:

public List<Id> oppList;

Note how you are creating a new Queueable in this line:

System.enqueueJob(new PrimaryCampaignQueueable(oppIds));

EDIT: I was right, but for a partly wrong reason.

As @sfdcfox says, it's because the static member is not serialized when the Queueable is serialized prior to processing.

When the execute method is called (by the system), the Queueable is rehydrated and the oppList, being static is lost - which is why it ends up null.

Here is the relevant documentation:

The Queueable interface behaves a lot like the Batchable interface. It gives you a class structure that we can serialize for you, and you can serialize more than just primitive arguments. It is called by a method, System.enqueueJob(), which returns a job ID to you.

developer documentation

2
  • 1
    Also the execute method shouldn't be static either. Commented Sep 8, 2017 at 2:28
  • 1
    @DavidCheng You can access static members from non-static code, but you can't access non-static members from static code. It compiles just fine, but only non-static, non-transient variables will be serialized.
    – sfdcfox
    Commented Sep 8, 2017 at 4:00

You must log in to answer this question.

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