Total Pageviews

Friday, May 25, 2012

Unit Test Methods - demystified

Unit Test Methods more often than not baffles new converts to salesforce as a platform. Even I had my share of hiccups. This prompted me to pen few simplistic lines on Unit Test Methods and to demystify them. 
I remember once someone in my Team asked me what are Unit Test Methods and I went ahead with a very bookish definition, the enquirer seemed a bit lost in the technical jargon. As I was trying to simplify the gist of my verbal diarrhea on Test Methods another colleague of mine described it as follows - It is something which visits all lines of your code w.r.t. if else statements and/or for loops . Quite simple isnt it?  
But for the sake of structure I will provide both perspective the technical bookish as well as the layman's simplistic version.
Unit Test Methods Defined
To facilitate and promote the development of robust, error-free code, Apex Code requires the creation and execution of unit tests. Unit tests are class methods that verify whether a particular piece of code is working properly. Unit tests are written in Apex Code and annotated with the testMethod keyword .
Why do we need Unit Test Methods
1. We need them for deploying code from either developer account or sandbox  to production.
2. Also in order to package and publish your code on AppExchange you need to provide coverage to your code using Test Methods.
What is coverage?
Test Methods are blocks of code or code statements which visit/passes the code written by you either in triggers or VF page and associated apex classes.
How much code coverage is  needed?
A minimum of 75% has to be covered to one's overall code in one's organization (salesforce instance)
What is the language used?
Its written using Apex.
What is the basic syntax used?
You can either write your Test Methods inline with your code using the "TestMethod" keyword . Alternatively you can group your test methods in Apex classes and use the @isTest keyword to point that the class being defined contains test methods.
Lets discuss these 2 approaches in detail.
  1. Defining a Test Method using the testMethod keyword - Lets assume you have a trigger on Account object.                                                                                                                                                   
    trigger myAccountTrigger on Account(before delete, before insert, after delete, after insert) {
    if (Trigger.isBefore) {
        if (Trigger.isDelete) {
    
            // In a before delete trigger, the trigger accesses the records that will be deleted with the Trigger.old list. 
        
            for (Account a : Trigger.old) {
                if (a.name != 'okToDelete') {
                    a.addError('You can\'t delete this record!');
                } 
            }
        } else {
    
        // In before insert or before update triggers, the trigger accesses the new records with the Trigger.new list. 
        
            for (Account a : Trigger.new) {
                if (a.name == 'bad') {
                    a.name.addError('Bad name');
                }
        }
        if (Trigger.isInsert) {
            for (Account a : Trigger.new) {
                System.assertEquals('xxx', a.accountNumber); 
                System.assertEquals('industry', a.industry); 
                System.assertEquals(100, a.numberofemployees);
                System.assertEquals(100.0, a.annualrevenue);
                a.accountNumber = 'yyy';
            }
    
    // If the trigger is not a before trigger, it must be an after trigger. 
        
    } else {
        if (Trigger.isInsert) {
            List<Contact> contacts = new List<Contact>();
            for (Account a : Trigger.new) {        
                if(a.Name == 'makeContact') {
                    contacts.add(new Contact (LastName = a.Name,
                                              AccountId = a.Id));
                }
            } 
          insert contacts;
        }
      }                                                                      }}}  
    
           Now in the above trigger you will notice that a) before insert b) before delete and c) after insert are being handled . So what does it imply in order to get the code executed or visited on the above trigger one needs to create and delete accounts. So our unit test method will look like this :-              
    public class myClass  {   static testMethod void myTest()      {      String brno='678900';      Account a = new Account();      a.Name='Test Account';      //a.Branch_Number__c = brno;      insert a;      delete a;    }  }
  2.                               
  3.  Now let me take an example of writing a separate class for Unit Test Methods and here I will take the example of a VF Page and its controller and how to provide coverage to the controller class.              Say my Visual Force page is called "MyController" and is associated with a custom controller by the name-"MyController"                                                                                                                            Code VF Page is as follows:-
    <apex:page controller="MyController" tabStyle="Account">
    <apex:form >
    <apex:pageBlock title="Hellooo{!$User.FirstName}">
    Your Account Name:
    <apex:inputField value="{!acc1.name}"/>
    <apex:commandButton action="{!save}" value="save"/>
    </apex:pageBlock>
    </apex:form> 
    </apex:page>         
                                                                                                                          Code of the controller is as follows:-                                                                                                    public class MyControllerAcc
     {
        public String save { get; set; }

     public Account[] acc1;
     public Contact SelectedCon;
     public Contact[] Arrcon ;
     public string SelectedConName;
     String[] SelectedAccName= new String[]{}; 
     public String Conid { get; set; }
     public String ConName { get; set; }
     public Boolean MasterBtnVis { get; set; }

    public MyControllerAcc() 
    {
    //Id aid = ApexPages.currentPage().getParameters().get('id');    
    acc1 = [select name,id from Account];
    MasterBtnVis   =false;
    }

    public Contact[] getcontacts()
    {
    if(SelectedAccName.size()>0)
    {
    Arrcon = [select firstname,id from Contact where accountid=:SelectedAccName];
    }
    else
    {
    //Arrcon = [select firstname,id,account.name from Contact];

    }
    return Arrcon;   
    }


    public List<SelectOption> getAcc1() 
    {    
    List<SelectOption> options = new List<SelectOption>();
    Integer i=0;
     for(Account acc: acc1)
       {
           options.add(new SelectOption(acc.id,acc.name));
       }
      return options;
     
    }

           public String[] getSelectedAccName() 
            {
                return SelectedAccName;
            }
                
            public void setSelectedAccName(String[] SelectedAccName) 
            {
                this.SelectedAccName= SelectedAccName;
            }

            public PageReference test() 
            {
                  MasterBtnVis=true;
                  return null;
            }
            
            public PageReference save() 
            {
             
                //if(ApexPages.currentPage().getParameters().get('cid')!= null)
                //{
                   SelectedCon = [select id, firstname from Contact where id =:conid];
                   SelectedCon.firstname=conname;
                  update SelectedCon ;
                  //}
                  
                  String Contactid = Conid;
                  return new PageReference('/' + Contactid ) ;
                  //return null;
            }
            public String getSelectedConName() 
            {
                return SelectedConName;
            }
            public boolean getMasterBtnVis() 
            {
                return MasterBtnVis;
            }
     
            public void setSelectedConName() 
            {
                SelectedConName = ApexPages.currentPage().getParameters().get('cid'); 
                //SelConName; 
                //return SelectedConName;
            }
            public Pagereference SetContactValues()
            {
                  Conid = Apexpages.currentPage().getParameters().get('cid');
                  ConName = Apexpages.currentPage().getParameters().get('cname');
                  return null;
            }
            public PageReference iWantMyJSValues() {
            conid= Apexpages.currentPage().getParameters().get('one');
            conname = Apexpages.currentPage().getParameters().get('two');
            return null;
        }
    } Code of my Unit Test Method for the above controller and page is as follows:- @isTest
    public class MyControllerTests
    {
     public static testMethod void testMyController()
     {
             //create instance or variable of your VF Page PageReference pageRef = Page.MyController;
            Test.setCurrentPage(pageRef);
            Account acc= new Account(name='Acme');
            insert acc;
            // Add parameters to page URL
            ApexPages.currentPage().getParameters().put('id', acc.id); //create instance or variable of your controller class         MyController controller = new MyController(); //set the values of public property controller.MasterBtnVis =false; controller.ConName ='TestName'; //invoke public methods of the controller class Account Acctmp=controller.getAcc1();
            System.assertEquals('Acme', Acctmp.Name);
            controller.Acc1.name='Changed';
             //invoke public methods of the controller class controller.save();
            Account[] accs = [select id, name from account where id =: acc.id];
            System.assertEquals('Changed', accs[0].name);
       
             }

    } So in a controller and a VF page one has to write code which emulates the invoking of the UI by a user. So how does one do that. Create an instance of the VF page as done in the above code and then set the parameters if the page needs parameters for being invoked. Secondly you can instantiate a variable of your controller class , set various public properties, invoke the public methods as highlighted in the sample test Method class above.
Hope this clears the confusion around test classes and in case you still face challenges feel free to post your queries.


3 comments:

  1. hey its a nice blog to understand test cases properly, as it explains for controllers as well as triggers.Good Job

    ReplyDelete
  2. Thanks ... Life for your kind words ...

    ReplyDelete