PageMessages & Testing

Recently I experienced some problems trying to test the page messages that were being added to a Visualforce page by subsequent controller calls. As this behaviour is a bit specific, I will try to explain how it works with examples.

For the example, we have a simple Visualforce page and a controller:

<apex:page showHeader="true" sidebar="true" controller="MyVisualforceController">
    <apex:pageMessages/>
    <apex:form>
        <apex:commandButton action="{!addMessage1}" value="addMessage1"/>
        <apex:commandButton action="{!addMessage2}" value="addMessage2"/>
        <apex:commandButton action="{!addMessage3AndClear}" value="addMessage3AndClear"/>
        <apex:commandButton action="{!addMessage4AndReload}" value="addMessage4AndReload"/>
    </apex:form>
</apex:page>

 

public with sharing class MyVisualforceController
{
    public PageReference addMessage1()
    {
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Message 1'));
        return null;
    }

    public PageReference addMessage2()
    {
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Message 2'));
        return null;
    }

    public PageReference addMessage3AndClear()
    {
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Message 3'));
        ApexPages.getMessages().clear();
        return null;
    }

    public PageReference addMessage4AndReload()
    {
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Message 4'));
        PageReference pageRef = ApexPages.currentPage();
        pageRef.setRedirect(true);
        return pageRef;
    }
}

 

In these tests I am going to use pageMessages tag. If you want to know more about the different options for showing messages in a Visualforce page, take a look at this great post from Jesse Altman.

As you see in the controller,  methods addMessage1() and addMessage2() simply add messages to the page. Method addMessage3AndClear() adds a message and tries to clear it after, and addMessage4AndReload() method adds a message and then performs a page reload. All these methods will be used later.

First we will try a simple test, adding ‘Message 1’ and, after that, adding ‘Message 2’. If we perform this test manually, pressing addMessage1 and addMessage2 buttons, when we press addMessage1, the first message will be added to the page:

message1

And when we press addMessage2, the first one will be deleted, and the second one added:

message2

If we write a unit test to check this, however, both messages will be added to PageMessages variable, as the context is not refreshed between the two requests inside a unit test. This test, which proves this fact, will pass:

@isTest
static void addMessage1_addMessage2()
{
    MyVisualforceController controller = new MyVisualforceController();
    controller.addMessage1();
    controller.addMessage2();

    ApexPages.Message[] messages = ApexPages.getMessages();

    System.assertEquals(2, messages.size());
    System.assertEquals('Message 1', messages[0].getDetail());
    System.assertEquals('Message 2', messages[1].getDetail());
}

 

What can we do to refresh PageMessages variable in my tests? Let’s try recreating the controller:

@isTest
static void addMessage1_recreateController_addMessage2()
{
    MyVisualforceController controller = new MyVisualforceController();
    controller.addMessage1();

    controller = new MyVisualforceController();

    controller.addMessage2();

    ApexPages.Message[] messages = ApexPages.getMessages();

    System.assertEquals(2, messages.size());
    System.assertEquals('Message 1', messages[0].getDetail());
    System.assertEquals('Message 2', messages[1].getDetail());
}

 

The test passes again. Recreating the controller doesn’t reset PageMessages.

Let’s try now to clear PageMessages after adding ‘Message 3’. Then no messages should appear, right? If we click addMessage3AndClear button, this is the result:

addMessage3AndClear

This is, ApexPages.getMessages().clear() won’t have any effect outside test context. If we write a unit test, the result will be the same, no effect from the clear() method inside a unit test:

@isTest
static void addMessage3_clear()
{
    MyVisualforceController controller = new MyVisualforceController();
    controller.addMessage3AndClear();

    ApexPages.Message[] messages = ApexPages.getMessages();

    System.assertEquals(1, messages.size());
    System.assertEquals('Message 3', messages[0].getDetail());
}

 

Let’s try then forcing a page reload. Supposedly, a page reload will flush everything in the viewstate. If we click addMessage4AndReload manually, it works, ‘Message 4’ is added but then deleted when reloading the page.

message4

Then, great! A page reload does reset PageMessages outside test context. If we write a unit test that simulates this call:

@isTest
static void addMessage4_reload()
{
    MyVisualforceController controller = new MyVisualforceController();
    controller.addMessage4AndReload();

    ApexPages.Message[] messages = ApexPages.getMessages();

    System.assertEquals(1, messages.size());
    System.assertEquals('Message 4', messages[0].getDetail());
}

 

The test passes again, this is, the PageMessages is not emptied when performing a page reload inside a test method.

So, after further investigations we find there is no way of clearing PageMessages inside a test method. In contrast, any real server request  will clear PageMessages, as the context changes and the PageMessages variable is initialized. There is an idea for changing this behaviour that you can vote.

I want to point out another particular behaviour of PageMessages in test context. What do you think it should happen if we call addMessage1(), after that addMessage2(), and after that addMessage1() again? Should PageMessages contain the three messages?

@isTest
static void addMessage1_addMessage2_addMessage1()
{
    MyVisualforceController controller = new MyVisualforceController();
    controller.addMessage1();
    controller.addMessage2();
    controller.addMessage1();

    ApexPages.Message[] messages = ApexPages.getMessages();

    System.assertEquals(2, messages.size());
    System.assertEquals('Message 1', messages[0].getDetail());
    System.assertEquals('Message 2', messages[1].getDetail());
}

 

And the answer is no. The test passes again. This means if we try to add a message to the PageMessages variable which was already contained inside it, it won’t be added again. This is, PageMessages variable acts as a Set, it will not contain duplicate messages.

 

 

 

One thought on “PageMessages & Testing

Add yours

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: