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.


Thursday, May 10, 2012

HP Public Beta Cloud Launched today!




HP Cloud is All Set to Enter the Public Beta. Though analysts say it has a long way to go before perfecting its cloud avatar. One of the things which caught my imagination was that in HP cloud one could migrate LAMP based application onto HP Cloud Compute Service!!!! It has primarily the constituents viz. :-
1. HP Cloud Compute
2. HP Cloud Object Storage
3. HP Cloud Content Delivery Network (CDN)
Enclosed is the brief overview by JANAKIRAM MSV . It also contains a video of the HP cloud. We can have head-sup in knowing the pos of SFDC over HP Cloud. Salesforce pros time to dive into the nuances of this new cloud offering - do a gap analysis vis-a-vis edge SFDC holds over HP or things which need to be factored onto the Salesforce cloud platform... 



HP Cloud is All Set to Enter the Public Beta

HP Cloud Video

Tuesday, December 6, 2011

Annotations

An annotation's dictionary meaning is an explanatory note added to a text. Similarly in Apex an Annotation
qualifies a method,class, suggesting how it can be used, invoked or qualifies it by stating if its a test method or
not. There are some 5 keywords which are used in conjunction with @ to serve as annotations of method , class , etc. definition.
Syntax of annotation:-
@ {Keyword name}
All Annotations are defined by prefixing the annotation keywords with @ symbol.

Apex supports the following annotations:

Deprecated - suggest the class ,exceptions, enums, interfaces,variables or methods in question is no more in use or will not be distributed in futher releases if its part of a package.The deprecated components dont show up in future installation of a package but reside normally for older installation (in which the element was not
deprecated when installed)

Code Exampleusing deprecated  :-
@deprecated
  // This method is deprecated. Use myOptimizedMethod(String a, String b) instead.
 
  public void myMethod(String a) {
 
}

Some points to ponder about deprecated annotation:-

  • Unmanaged packages cannot contain code that uses the deprecated keyword.
  • When something in Apex, or when a custom object is deprecated, all global access modifiers that reference the deprecated identifier must also be deprecated. 
  • webService methods and variables cannot be deprecated.
  • You can deprecate an enum but you cannot deprecate individual enum values.
  • You can deprecate an interface but you cannot deprecate individual methods in an interface.
  • You can deprecate an abstract class but you cannot deprecate individual abstract methods in an abstract class.
  • You cannot remove the deprecated annotation to undeprecate something in Apex after you have released a package version where that item in Apex is deprecated.


Future
Use of future annotation along with a method suggests that it can be called asynchronously.Methods with the future annotation must be static methods, and can only return a void type.

Example of future annotation:-

global class MyFutureClass {

  @future
  static void myAsyncMethod(String a, String b) {
    System.debug('Method called with: ' + a + ' and ' + b);
    //do callout, other long running code
 
  }
}

Also one can additionally specify or qualify whether the method can make a callout or not by adding 
(callout=false/true).

Points to be careful about with @future:-
1. The getContent and getContentAsPDFPageReference methods cannot be used in methods with the future annotation.
2. You cannot call a method annotated with future from a method that also has the future annotation.


IsTest
This Annotation tells the system that code enclosed in a method or class is aimed at testing one's application or in other words giving coverage to classes and triggers.

Example IsTest annotation:-

@isTest
private class MyTestClass {

   // Methods for testing
 
   @isTest static void test1() {
      // Implement test code creating , editing or deleting data
 
   }

   @isTest static void test2() {
      // Implement test code
 
   }

}
Things to be careful about IsTest annotation:-
1. Classes defined as isTest can't be interfaces or enums.
2.Methods of a public test class can only be called from a running test, that is, a test method or code invoked by a test method, and can't be called by a non-test request.

Also one can additionally specify or qualify whether the class/method will be executed during package installation by adding (OnInstall=true/false).

ReadOnly
The @ReadOnly annotation allows us to execute unrestricted queries against the Force.comdatabase, but prevents us from making DMLoperations calls to class.schedule, asynchronous calls that is calls to methods qualified with @future and sending emails.This is used to work with large datasets without data manipulation.

RemoteAction
Used in Javascript remoting.The RemoteAction annotation provides support for Apex methods used in Visualforce to be called via Javascript.

Things to bear in mind about RemoteAction annotation:-
1. Methods with the RemoteAction annotation must be global and static methods.

Basic syntax of remoting (RemoteAction) :-

@RemoteAction
global static String getItemId(String objectName) { ... }

Also we have Apex REST Annotations
Six new annotations have been added that enable you to expose an Apex class as a RESTful Web service
@RestResource(urlMapping='yourUrl')
@HttpDelete
@HttpGet
@HttpPatch
@HttpPost
@HttpPut

Wednesday, November 30, 2011

Aloha, Milestones PM rewards for finding their flaws- a unique methodology !!!!


Force.com Labs announced that Milestones PM is now available as both an Unmanaged App and as a Managed Aloha App.  That’s right: Milestones PM is now available for professional edition and won’t count against your Objects, Apps and Tabs limits in other editions.
Secondly, till November 30 (aka Cloudforce NYC Day) {they announced a unique of UAt by actual users }, Salesforce Labs is pleased to announce we are offering a bug bounty on either Milestones PM package, version 1.55 (unmanaged) or 1.50 (managed) and later.  Submit a bug to our Github repo issues tracker, and, once we verify it,  we’ll send you a $10 Starbucks gift card.  
Milestones PM remains open source and — and I’m perhaps unreasonably excited about this — we use a single code base for both packages. 
Cool down the incentive for finding bugs is over but nevertheless  a unique approach to quality assurance kudos....

Friday, November 4, 2011

Why is the selection between Lookup and Master-detail relationship so crucial


Today as I was surfing through questions in the SF community , I came across a query which was related to the summary functions between two object linked with a lookup relationship. The query was simple all the respondent wanted to know was for:-
1. Two objects A and B linked with  lookup relationship how to find out the count of the object B linked to object A.
2. Also if it was possible to find sum of a particular currency field in B for all the instances of B linked to a particular instance of  A.
3. To do the same through configurations rather than any code
Some pro-s may say come on that is not possible for a "lookup" relationship!!! True  to achieve that one either :-
1. Needs to link the two objects with what we call the "Master-Detail" relationship.
2. Else if that is not feasible one may seek the intervention of code and a good old trigger will do the trick.
So very correct but this brings up the more basic question of :-
1. Planning - A product or project should be planned and designed very carefully and should undergo many iterations of brain storming before it actually goes under the hammer-specially when its ERD and database schema is being finalized.
2. Mapping of Objects and the relationships between them should be accorded utmost scrutiny and logical evaluation.
I remember during my early days with salesforce and managed package (remember it freezes the schema and makes any change very difficult and exhaustive process from the point of view of product maintenance) a small slip in the selection between a lookup and master-detail relationship compelled me to write a trigger which could otherwise would have been easily achieved with standard configurations.
Hence the time devoted to database design or in simpler terms "of deciding the way objects are linked with one-another" may appear irrelevant to some but its worth the trouble and will spare us from product maintenance nightmares.

Tuesday, August 30, 2011

Allaying apprehensions of Data Security w.r.t. Salesforce

One of the questions posed to all the business consultants showcasing the versatility of Salesforce CRM to prospective clients ,"is the concern of data security" and the traditional fear of keeping one's sensitive data in the custody of "Salesforce Servers". Assuring the client about the flawless of the security of data becomes a torturous exercise - walking the clients through:-
1. The "Trust Statements"
2. Certification of respective government
3. Global Audit compliance
The jargon involved in these documents is so complicated and lengthy that the actual intent {assuring about of safety of data} is lost on the audience.
My simple explanation to the business team {which occasionally directs the volley of these queries towards the technical team} is to explain the dynamics of "Data Safety" by way of a simple analogy. Here is how it goes - Whenever prospective clients or existing clients are showing their concern about safety of their data, ask them a simple question? Sir/Mam where do you keep your valuables or money, the obvious answer to this query would be a bank. (unless it is ill-gotten) Why do you keep it with bank? As you know your money is safer there and banks are better equipped and legally bound to protect your money and also replace it in case of theft. The same spirit applies to CRMs - they too are legally bound to safeguard the privacy and safety of your data. Since you would not keep your money at home and prefer a bank instead and will not be deterred by the logic that it implies entrusting a third party with your money (as you are assured of its safety) the same logic applies to your "data" by way of corollary when you store it on "salesforce servers". If you want more insight to this please go through:-
1. Safe Harbor statements provided here -http://trust.salesforce.com/trust/privacy/tools/
2. Also the most valuable testimony of our (salesforce crm's ) commitment to data security and  privacy can be provided by existing client base , refer to the link enclosed for more info. http://trust.salesforce.com.
3. Besides salesforce provides the option of trial runs , one can use the CRM on a trial basis from Appexchange and once assured you can go ahead and subscribe.
Hope this settles the noise around the data security debate vis-avis "The Cloud" (common name of salesforce).