9

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.

4 Answers 4

6
+50

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.

2
  • 1
    You could probably use 'instanceof' operator instead of toString, try using system.assertEquals(true, pblk.childComponents[0] instanceof Component.apex.panelgrid); // correct type? Sep 8, 2013 at 0:04
  • Thanks @AndrewFawcett, you are entirely correct. I've updated my answer to use instanceOf. I did, however, use System.assert( {boolean test} ) instead of System.assertEquals(true, {boolean test} ). Sep 8, 2013 at 17:20
1

You may be aware that if you try to include these dynamically created components in the view state you get a "Not Serializable" error. But as the components cannot therefore be in the view state, an error such as an invalid date format being entered causes the current values of all the fields to be lost because the component tree is missing which is very likely to annoy your users. (Appears to be JavaServer Faces or similar underneath.) Salesforce support state that this is "by design".

So arguably where forms are concerned, it's better to avoid the dynamic components altogether rather than test them in depth...

0

The following is what i generally follow for testing these

Asssume i have a method below for dynamic component ,

 public Component.Apex.pageBlock getstartpage(){

    if(exam.Examination_Status__c==Label.Label_for_New_Examination_Status){

        Component.Apex.pageBlock pg=new Component.Apex.pageBlock(id=ApplicationConstant.PANEL);//formation of pageblock

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

        Component.Apex.outputText outtx1=new Component.Apex.outputText();
        outtx1.value=ApplicationConstant.STARTMESSAGE;

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

        Component.Apex.CommandButton startbutton=new Component.Apex.CommandButton();
        startbutton.Value=ApplicationConstant.STARTEXAM;
        startbutton.expressions.action=ApplicationConstant.STARTACTIONEXPRESSION;
        Set<String> setre=new Set<String>();
        setre.add(ApplicationConstant.PANELPROG);
        setre.add(ApplicationConstant.THEFORM);
        startbutton.reRender=setre;

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

        return pg;
       }else {
         return null;
     }

}

The following is my test class

 Component.Apex.pageBlock pblk=controller.getstartpage();

    //Assert that the PageBlock is rendered since the Status is "New" 
    System.assert(pblk!=null);

This is the best we could do i feel while testing these

2
  • 1
    Thank you, though I was hoping to be able to test that the rendering was correct, not just that something existed.
    – user3700
    Aug 30, 2013 at 17:20
  • lets see if any one in community has better solution and innovative technique to do this .thanks Aug 30, 2013 at 18:43
0

Yes even i also came across the same situation. It seems there is no reference on how to do that. But having said that we have to manually traverse and assert. Sounds kludgy but it might help.

Method in controller:

    public Component.Apex.PageBlock getActionForm()
    {
    Component.Apex.PageBlock pb = new Component.Apex.PageBlock();
    Component.Apex.OutputPanel pl = new Component.Apex.OutputPanel();
    pl.id = 'thePanel';
    Component.Apex.InputField name = new Component.Apex.InputField();
    name.expressions.value = '{!Account.Name}';
    name.id = 'name';
    Component.Apex.OutputLabel label = new Component.Apex.OutputLabel();
    label.value = 'Name';
    label.for = 'name';
   Component.Apex.CommandButton save = new Component.Apex.CommandButton();
    save.value = 'Save';
    save.expressions.action = '{!Save}';
    pb.childComponents.add(pl);
    pl.childComponents.add(label);
    pl.childComponents.add(name);
    pl.childComponents.add(save);
    return pb;
    }

Test Class

     Component.Apex.PageBlock pb = contr.getActionForm();
     System.assertEquals(pb.childComponents[0].id, 'thePanel');

You must log in to answer this question.