Search

Tuesday 16 August 2011

Correct Way For Creating Unit Tests

When you deploy code to live your unit tests may work because there is data in the live environment. But, lets say a load of data is archived away, or as the status’s of data can change from say Pending to Active for example etc, maybe your soql in your unit tests which normally would return data it now doesn’t. So if the data is not there and your unit tests don’t create data then your units tests will fail and you won’t be able to deploy anything to live until its fixed.
Also if you refresh a sandbox, something you should do frequently; not all the data from live will populate your sandbox. So there is likely to be missing data. When you then start working on an existing class and you want to see if the unit tests pass, but they fail, because there’s no data in the sandbox and your test classes haven’t created test data. So you either have to find which data needs to be inserted and add the data to the sandbox, which you will have to remember to do this every time you refresh your sandboxes, or change the unit tests so that they create test data, the latter being the best option.
So as you develop you must deploy your code to an empty Sandbox where there is no data and run all your unit tests to prove that your unit tests create test data correctly.
So your unit tests must search for existing data and if it doesn’t find the data it must create the test data. So you might have something like this:

Function void createdata(){
list<Testdata__c> defaultEmail = new list<Testdata__c>();
defaultEmail = [Select ID__c,DefaultValue__c From Testdata__c Where ID__c='Default'];
    
      //if soql returns nothing then create otherwise update
      if (defaultEmail.size() == 0){
//data doesn’t exist so create new data
Testdata__c newTestdata = new Testdata__c(ID__c='DefaultVal',DefaultValue__c='test@aol.com');
          
            insert newTestdata;   
      }
      else{
            //data exists so update it
            defaultEmail[0].DefaultValue__c = 'test@aol.com';
            update defaultEmail;
      }
}
    
      This code will not go in the unit test class, but in a separate class dedicated to creating test data so that this class can be used and shared by multiple test classes, lets call this class TestDatasCreation class. The only problem with this TestDatasCreation class is that it must also have unit test coverage. This is because the data will either exist or not exist in live, so without the TestDatasCreation  class having its own unit test you will only test 1 part of the if statement block in the code above, where data exists or doesn’t.
      To make sure that both if statement blocks are tested you could delete the data in the Testdata__c first to ensure that the 1st part of the if statement block is tested.

Delete defaultEmail; ( remove the data )
However in some objects deleting all the data in live this could break governor limits and also could cause your unit tests to take longer to run. Remember all unit tests are run when deploying code so delete statements like that will slow down deployments.
Instead if we change how the function is setup:

Function void createdata(String searchValue){
list<Testdata__c> defaultEmail = new list<Testdata__c>();
defaultEmail = [Select ID__c,DefaultValue__c From Testdata__c Where ID__c=: searchValue];

…..

}

Now if we assume that normally the 2nd part of the if statement block is tested by unit tests already created. To ensure that the TestDatasCreation class has also full unit test coverage:

      //delete data first
list<Testdata__c> defaultEmail = new list<Testdata__c>();
defaultEmail = [Select ID__c,DefaultValue__c From Testdata__c Where ID__c='Default'];

            Delete defaultEmail;

            //1st part if statement block runs
Createdata(‘default’);

defaultEmail = new list<Testdata__c>();
defaultEmail = [Select ID__c,DefaultValue__c From Testdata__c Where ID__c='Default' limit 1];
    
//test created data
system.assert(defaultEmail[0].ID__c,'DefaultVal');


Of course the unit test for the TestDatasCreation class could be created to test both parts of the if statement block.

This will now give 100% test coverage for the TestDatasCreation class.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.