[SalesForce] How to unit test an Apex controller method for dynamic components

I have an Apex controller method that returns a Component.Apex.OutputPanel for use in a <apex:dynamicComponent /> tag. How do I do a unit test that asserts that the controller is outputting the correct HTML in the OutputPanel? That is, in my test I call the method and can make sure that the OutputPanel is non-null, but I can't find documentation on what properties the OutputPanel has other than the attributes one would use if creating one in code. I would have expected either some kind of .toHTML() or a meaningful .toString() or something, or ideally some way of accessing the object model that the OutputPanel represents, something like a DOM I guess. I feel like I must just be missing something obvious. What am I missing? What's the best way to unit test the code for an Apex Controller Dynamic Component?

Thank you.

Edit 9/3/13: I think the piece I'm missing is mainly the type library for the built-in force.com classes. That is, in my test I can take the output of my controller methods and call .childComponents in order to get a List<ApexPages.Component>, but nowhere can I find a list of methods for what I can do with an ApexPages.Component object. Is there a page in the documentation that I just keep on missing? I feel like I have to be the only person trying to unit test dynamic components, which doesn't make any sense. Thanks again.

Best Answer

Even if available, I wouldn't recommend testing against the results of .toHtml() - if you've ever looked at the html output of a VF page, you know that there is lots of extra bits added by the system - ids, classes, event handlers - that aren't guaranteed to not change. You are much better off inspecting and asserting against the object returned.

nowhere can I find a list of methods for what I can do with an ApexPages.Component object

The list is available, but it isn't very helpful. In particular, .expressions (and .facets, I would guess, although I did not test it) appears to be write-only. This leaves us with .childComponents, but that's enough to traverse the tree of VF objects returned. So how to ensure that the results are correct?

I'd point out as a side note here that the degree to which you need to test depends on the amount of logic in your dynamic code. For example, in the sample code in @[Mohith Kumar]'s answer (and which my sample code, below, is based on) really only has one thing that needs to be tested - the conditional. Confirming that the method does or doesn't return a component is enough - the rest of the code that builds the pageBlock is basic VF; you should be testing your application logic, not the services provided by the platform.

That said, there will of course be more complex code where many parts of the dynamic response may depend on the inputs, and so it may be necessary to assert much of the response. There are three tools we can use to do so:

  1. We can use .childComponents() to traverse the tree of VF components returned.
  2. We can use the instanceOf operator to confirm that the VF components in the tree are of the expected type, allowing us to confirm (for example) that we got a grid instead of a table.
  3. We can cast any ApexPages.Component to it's actual type, allowing us to access all of the properties of the object.

Here's a simple demo controller, based on the sample code in Mohith's answer:

public class DynTestPageController{

    public Component.Apex.pageBlock getStartpage(){

        if(true){ //this would be a real conditional in real code.

            Component.Apex.pageBlock pg=new Component.Apex.pageBlock(id='PanelId', title='Main Block');//formation of pageblock

            Component.Apex.panelGrid pgrid=new Component.Apex.panelGrid(id='GridId');//formation of panelGrid
            pgrid.columns=3;

            Component.Apex.outputText outtx1=new Component.Apex.outputText();
            outtxt1.value='Welcome to the start page';

            Component.Apex.outputText outtxt2=new Component.Apex.outputText();
            outtxt2.value='';

            Component.Apex.CommandButton startbutton=new Component.Apex.CommandButton();
            startbutton.Value='Begin';
            startbutton.expressions.action='{!save}';

            pg.childComponents.add(pgrid);
            pgrid.childComponents.add(outtxt1);
            pgrid.childComponents.add(outtxt2);
            pgrid.childComponents.add(startbutton);

            return pg;
        }else {
            return null;
        }
    }    
}

And here's a test class to validate the dynamic VF generated:

@isTest
public class DynTestPageControllerTest{

    @isTest
    public static void testStartpage() {
        DynTestPageController controller = new DynTestPageController();
        Component.Apex.pageBlock pblk=controller.getstartpage();      

        system.assertNotEquals(null, pblk); //did we get a component?
        system.assertEquals(pblk.title, 'Main Block'); //assert component property
        system.assertEquals(1, pblk.childComponents.size()); //do we have correct # of children

        //first (and only) child
        system.assert(pblk.childComponents[0] instanceOf Component.apex.panelgrid); // correct type?
        Component.apex.panelgrid pgrid = (Component.apex.panelgrid)pblk.childComponents[0];  // cast it to access props
        system.assertEquals(3, pgrid.columns); //assert child component property
        system.assertEquals(3, pgrid.childComponents.size()); // continuing down the tree...

        //first child of panelgrid
        system.assert(pgrid.childComponents[0] instanceOf Component.apex.outputtext);
        Component.apex.outputText ot = (Component.apex.outputText)pgrid.childComponents[0];
        system.assertEquals('Welcome to the start page', ot.value);

        //second child of panelgrid
        system.assert(pgrid.childComponents[1] instanceOf Component.apex.outputtext)
        ot = (Component.apex.outputText)pgrid.childComponents[1];
        system.assertEquals('', ot.value);

        //third child of panelgrid
        system.assert(pgrid.childComponents[2] instanceOf Component.apex.commandbutton);
        Component.apex.commandButton btn = (Component.apex.commandButton)pgrid.childComponents[2];
        system.assertEquals('Begin', btn.value);
        system.assertEquals('#{save}', btn.action.getExpression());


    }            

}

Of course, you may need a little experimentation and cave-man debugging (a.k.a., System.Debug() calls) to determine the correct way to test some things. For example, see the last assert above - Originally, I was asserting for '{!save}', but apparently it is internally stored as '#{save}'.

Updated 8 Sep 2013 to use instanceOf for type checking instead of toString. Thanks to @[Andrew Fawcett] for the suggestion.

Related Topic