13

I've got a class which acts as a controller extension. The class is designed to add a number of fields to the StandardController so a component used on the page can dynamically access some fields.

However, when I went to test the class, I encountered the error:

System.SObjectException: You cannot call addFields when the data is being passed into the controller by the caller.

I tried using reset():

System.SObjectException: You cannot call reset when the data is being passed into the controller by the caller.

As far as I can tell, the data is fully initialized before it is passed along to the next object. I'm at a bit of a loss on how to cover these lines. The code below is an example that exhibits the same behavior, right now, the code I am using I just skip the line using Test.isRunningTest, but I'd like a solution to provide total coverage.

Extension Class

public class TestController_extension {  
    public TestController_extension(ApexPages.StandardController standardController) {
        standardController.addFields(new List<String>{});
    }
}

Test Class

@isTest
private class TestController_extension_test {
    @isTest
    private static void test() {
        Account a = new Account(); 
        ApexPages.StandardController controller = new ApexPages.StandardController(a);
        TestController_extension extension = new TestController_extension(controller); 
    }
}

1 Answer 1

17

Indeed you cannot call addFields from a test. This is one of the very few cases where you just have to check Test.isRunningTest.

You will get total coverage, however, if you one-line it. Declare your list separately, and then call addFields on the same line as your check:

public Account record { get; private set; }
public MyExtension(ApexPages.StandardController controller)
{
    List<String> fields = new List<String> { /*fields*/ };
    if (!Test.isRunningTest()) controller.addFields(fields); // still covered
    record = (Account)controller.getRecord();
}

Note that when you pass an SObject to a StandardController, it will still have all of its fields defined, so for the most part you don't need to worry about query fields when testing.


There are a few alternatives which I do not typically use, but have seen around. You might prefer to just query for the data yourself:

public Account record { get; private set; }
public MyExtension(ApexPages.StandardController controller)
{
    record = [SELECT ... FROM Account WHERE Id = :controller.getId()];
}

And one final option which I find more distasteful, you can make sure the fields are included in your markup instead. Imo this approach mixes concerns in an ugly way, but it does work.

<apex:inputHidden value="{!record.FieldToQuery}" />
2
  • can you explain how to pass the record in the test? what are the concerns of adding the product in the visualforce page? thanks
    – jaw999
    Jul 5, 2017 at 19:36
  • Of the three possible solutions listed here IMO, the first and last are the best, because in both cases the field(s) will be available via controller.getRecord(). The middle approach (explicit query) circumvents controller.getRecord() to retrieve the record to be rendered. I think that's one of the main benefits of using the StandardController; the controller knows how to bootstrap the record based on the Id of the object, and the field bindings present on the VF page.
    – tlfu
    Aug 1, 2017 at 10:51

You must log in to answer this question.

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