Pages

Thursday, September 25, 2008

Getting Good Test Coverage on VF Pages



Once the development phase of your new Visualforce page is finished often many find it a challenge to receive the acceptable code coverage needed to be obtained in order to transport the changes into production instance of Sales force.

Below is a sample of how you can create a test class that will take care of it! This is going to be really interesting...

First i will show you the visual force page and the controller behind it.
What this page does is to simply filter the Account records by their type (picklist) and show them to the user.


Here is the source code for the Visualforce page and its controller:



<apex:page controller="myPageController" tabStyle="Account">
<apex:form >
<apex:sectionHeader title="My Page" />
<apex:pageBlock title="" id="pageBlock">
<apex:pageBlockButtons location="top">
<apex:inputField id="accountType" value="{!Account.Type}" />
<apex:commandButton value="View" action="{!ViewAccounts}" id="theButton" rerender="pageBlock"></apex:commandButton>
</apex:pageBlockButtons>
<apex:pageMessages ></apex:pageMessages>
<apex:pageBlockTable value="{!accounts}" var="a" rendered="{!NOT(ISNULL(accounts))}">
<apex:column >
<apex:facet name="header">Account Name</apex:facet>
<apex:outputLink value="/{!a.Id}" target="_blank">{!a.Name}</apex:outputLink>
</apex:column>
<apex:column value="{!a.type}"></apex:column>
</apex:pageBlockTable>
</apex:pageBlock>
</apex:form>
</apex:page>



and now the code for the Controller class:

public class myPageController {

private Account acc = new Account();

private List<Account> accs;

public Account getAccount()

{

return acc;

}

public void setAccount(Account a)

{

acc = a;

}

public List<Account> getAccounts()

{

return accs;

}

public PageReference ViewAccounts()

{

if (acc.Type == null)

{

//view a message because validation has failed.

ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'Validation Failed: Select an item fron the dropdown list.'));

return null;

}

string AccountType = acc.Type;

accs = [Select id, name, type from Account where type= :AccountType limit 20];

if (accs == null || accs.size() == 0)

{

ApexPages.addmessage(new ApexPages.message(ApexPages.severity.INFO,'No data found to be shown.'));

}

return null;

}

}











Now it is the moment! The most important part: the tester class.
In order to get a the best results following things should be included in your test method:
  • Create a reference to your page and set the Test object's current reference to your page (shown below)
  • Create a new instance of your controller (if you are using a Custom Controller or Extension)
  • Consider all possible scenarios (Test Cases) and try calling all properties and methods of your controller (Demonstrated below)


public class testMyPage {

static testMethod void myPage_Test()

{

//Test converage for the myPage visualforce page

PageReference pageRef = Page.MyPage;

Test.setCurrentPageReference(pageRef);

// create an instance of the controller

myPageController myPageCon = new myPageController();

//try calling methods/properties of the controller in all possible scenarios

// to get the best coverage.

Account pAccount = myPageCon.getAccount();

//test when type == null

myPageCon.viewAccounts();

//test when type = 'undefinedType'

pAccount.Type = 'other';

myPageCon.setAccount(pAccount);

myPageCon.viewAccounts();

// test when having results

pAccount.Type = 'Customer';

myPageCon.setAccount(pAccount);

myPageCon.viewAccounts();

myPageCon.getAccounts();

}

}


8 comments:

  1. Wonderful article Sam !!
    By anychance do you know whether is it good to have 2 test classes for a single controller class. The reason being, my controller class is 700 lines long and contains lots of conditions. When I tried to write test methods for all the conditions, the test class became very huge and hence throwed an error saying that the class is huge. So now I plan to split the test methods between 2 different classes. Is this a good practise ? Is there any other alternative you would suggest ?



    Thanks.

    ReplyDelete
  2. I believe that is possible.

    You may also want to divide your heavy visualforce page into smaller components (some that might be reusable later on) and then have separate classes to test the components out.

    Having the code divided into smaller pieces (Components) makes all more manageable later on when you want to support/troubleshoot or improve (add/remove feature) your page.
    Cheers,
    Sam

    ReplyDelete
  3. very helpful, created my first visualforce page test method in minutes using this simple guide :)

    Thank's alot!

    ReplyDelete
  4. its a good one to understand how to write test class methods

    ReplyDelete
  5. Test.setCurrentPageReference(pageRef);
    above line gives a error............
    Error: Compile Error: Method does not exist or incorrect signature: Test.setCurrentPageReference(System.PageReference) at line 9 column 1

    ReplyDelete
  6. Test.setCurrentPageReference(pageRef);
    above line gives a error.............


    Compile Error: Method does not exist or incorrect signature: Test.setCurrentPageReference(System.PageReference) at line 9 column 1

    ReplyDelete
  7. Awesome post......Thanks alot Sam....helps a lot for the beginners in writing test class

    ReplyDelete
  8. can u help in writing test code for the below code


    Visual for page:






    Search Text












    {!l.Name}











    {!l.Name}














    apex controller:

    public class wildcardSearchController{
    public String searchText{Set;Get;}
    List results;
    List Contactresults;

    public List getresults() {
    return results;
    }

    public List getContactresults() {
    return Contactresults;
    }

    public PageReference doSearch() {
    results = (List)[FIND :searchText RETURNING Lead(Id, Name, Email, Phone,Company,Status)][0];
    Contactresults = (List)[FIND :searchText RETURNING Contact(Id,Name,Account.Name, Department,Phone,Email)][0];
    return null;
    }


    }

    ReplyDelete