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 {
    public static FundTotals getCosts(Id id) {
        ccprm__FundsRequest__c request = [SELECT
                                            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)

private class FundsRequestCostsControllerTest {
    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;


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

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


In the Logs, I see that...

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


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


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


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)?
  • Your question seems incomplete in a few spots.
    – Adrian Larson
    Nov 3, 2016 at 17:17
  • 1
    I suspect that's because the campaign rollup fields are not calculated in real-time; on the detail page of a real campaign, you'll see a little notice that reads something like "may be delayed by 10 minutes."
    – sfdcfox
    Nov 3, 2016 at 17:19
  • the code formatting was having an issue with the < symbol in the for loop. I've fudged it for readability Nov 3, 2016 at 17:21
  • @sfdcfox : I see... Any suggestions to get around this? Also, any insight on the second issue? Why is it getting set as null instead of 0? Nov 3, 2016 at 17:22
  • @IanTunbridge I don't, actually. However, I don't have time to replicate this right now, so maybe someone else in the community can help.
    – sfdcfox
    Nov 3, 2016 at 17:34

1 Answer 1


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.

  • Yes, I second using the same name and this.property = property. Keeps code cleaner imo.
    – Adrian Larson
    Nov 4, 2016 at 13:09

You must log in to answer this question.

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