[SalesForce] Apex Class Object Constructor – Objects returns with null values

I'm new to SalesForce development. I have a basic Controller method that takes an Id for a "child" record (FundsRequest__c) and returns a Custom object that I define in the Controller class (FundTotals).

The custom object has a constructor method that I'm using to instantiate for the response for the method. For some reason, the values aren't getting set and instead a new FundTotals object is returned with null values for all of the properties. Here's my code:

FundsRequestCostController.cls: (Class under test)

public with sharing class FundsRequestCostsController {
    @AuraEnabled
    public static FundTotals getCosts(Id id) {
        ccprm__FundsRequest__c request = [SELECT
                                            ccprm__TotalCost__c,
                                            ccprm__RequestedAmount__c,
                                            ccprm__Campaign__r.NumberOfOpportunities,
                                            ccprm__Campaign__r.NumberOfWonOpportunities
                                            FROM
                                                    ccprm__FundsRequest__c
                                            WHERE Id = :id];

        FundTotals ft =  new FundTotals(request.ccprm__Campaign__r.NumberOfOpportunities, request.ccprm__Campaign__r.NumberOfWonOpportunities, request.ccprm__TotalCost__c, request.ccprm__RequestedAmount__c);
        System.debug(LoggingLevel.DEBUG, 'FundTotals: ' + ft);
        return ft;

    }

    public class FundTotals {
        public Integer NumberOfOpportunities;
        public Integer NumberOfWonOpportunities;
        public Decimal TotalCost;
        public Decimal RequestedAmount;

        public FundTotals(Integer numberOfOpportunities, Integer numberOfWonOpportunities, Decimal totalCost, Decimal requestedAmount) {
            NumberOfOpportunities   = numberOfOpportunities;
            NumberOfWonOpportunities= numberOfWonOpportunities;
            TotalCost               = totalCost;
            RequestedAmount         = requestedAmount;
            System.debug(LoggingLevel.DEBUG, 'FundTotals(constructor: ' + this);
            System.debug(LoggingLevel.DEBUG, 'numberOfOpportunities: ' + numberOfOpportunities);

        }
    }
}

FundsRequestCostControllerTest.cls: (Unit test)

@IsTest
private class FundsRequestCostsControllerTest {
    @IsTest
    static void testBehavior() {
        Campaign campaign = new Campaign();
        campaign.Name = 'test campaign';
        insert campaign;

        for(Integer i=0; i (lessthan) 10; i++){
            Opportunity opportunity = new Opportunity();
            opportunity.Name = 'test opp' + i;
            opportunity.Amount = 100 + i;
            opportunity.StageName = 'Closed Won';
            opportunity.CloseDate = date.today();
            opportunity.Campaign = campaign;
            insert opportunity;
        }

        ccprm__FundsRequest__c request = new ccprm__FundsRequest__c();
        request.ccprm__TotalCost__c = 1000.00;
        request.ccprm__RequestedAmount__c = 2000.00;
        request.ccprm__Campaign__c = campaign.Id;

        Account account = new Account();
        account.Name = 'test account';

        insert account;

        request.ccprm__Account__c = account.Id;

        insert request;

        Test.startTest();

        FundsRequestCostsController.FundTotals fundTotals = FundsRequestCostsController.getCosts(request.Id);

        Test.stopTest();
        System.assertEquals(10, fundTotals.NumberOfOpportunities);
        System.assertEquals(6, fundTotals.NumberOfWonOpportunities);
        System.assertEquals(10, fundTotals.RequestedAmount);
        System.assertEquals(10, fundTotals.TotalCost);
    }
}

Results:

In the Logs, I see that…

System.Debug(... 'FundsTotals: ' + ft)

returns:

|USER_DEBUG|[20]|DEBUG|FundTotals: FundTotals:[NumberOfOpportunities=null, NumberOfWonOpportunities=null, RequestedAmount=null, TotalCost=null]

Also…

System.debug(... 'numberOfOpportunities: ' + numberOfOpportunities)

returns:

DEBUG|numberOfOpportunities: 0

I'm confused by a few things:

  1. Why is the value of the parameter 0? (should be 10 with how I've created 10 opportunities in a for loop)?
  2. Why aren't the values (0) assigned to the FundTotals object (returns with null)?

Best Answer

Because Apex is case insensitive this:

public FundTotals(Integer numberOfOpportunities, ...) {
    NumberOfOpportunities = numberOfOpportunities;
    ...
}

is assigning the method parameter to itself.

Instead you need to assign the parameter value to the field like this:

public FundTotals(Integer numberOfOpportunities, ...) {
    this.numberOfOpportunities = numberOfOpportunities;
    ...
}

(You could also use different names, but personally I prefer to use the same name and keep a look out for missing this..)

The numberOfOpportunities is zero because that is the automatically assigned default value for that type of field.

Related Topic