+ Start a Discussion
Waqas AliWaqas Ali 

Increase code coverage of Schedulable test class ?

I have a Schedulableclass which is scheduled to execute every night at 12:00 AM,  here is my class, 
global class SalesOrderSchedule implements Schedulable
{

global static void checkrun()
    {
 String day = string.valueOf(system.now().day());
                String month = string.valueOf(system.now().month());
                String hour = string.valueOf(system.now().hour());
                String minute = string.valueOf(system.now().minute());
                String second = string.valueOf(system.now().second());
                String year = string.valueOf(system.now().year());

                String JobName = 'Scheduled Job of sales Order';
                String strSchedule = second +' ' + minute+' '+ hour+' '+ day+' '+month+' ?'+' '+ year;
                system.schedule(JobName, strSchedule, new SalesOrderSchedule());    
    }    
  global void execute(SchedulableContext sc)
  {
  Opportunity opp;
    
  for (Sales_Order__c salesorders : [select id, Name, LastModifiedDate,    Quote__c, OrderNum__c from Sales_Order__c WHERE LastModifiedDate  >= LAST_N_DAYS:1])
         {
             String OppNewStage=null;
             if (salesorders.OrderNum__c=='QUOTE WON')
             {
             OppNewStage='Closed Won';
              }
             else if (salesorders.OrderNum__c=='QUOTE LOST')
             {
             OppNewStage='Closed Lost';
             }
             else 
             {
              OppNewStage='Quoted';
             }
             
       opp=[select id, Name, StageName from Opportunity where id=:salesorders.Quote__c]; 
     opp.StageName=OppNewStage;
     update opp;
     
    
     }
     

   
  }
}

here is my test class,  my code coverage is 13%. 
@isTest
public class SalesOrderScheduleTest {
    static testMethod void myTestMethod() {        
         test.starttest();
     SalesOrderSchedule.checkrun();
     
   String chron = '0 0 12 15 9 ? 2015' ;        
         system.schedule('job', chron, new SalesOrderSchedule());
         test.stopTest();
    }
}

 

Static checkrun method is not covering, how to increase the code coverage ?? Guys help 

Best Answer chosen by Waqas Ali
ClintLeeClintLee
Hi Waqas,

You have a couple of issues with your execute method that I'd advise modifying.  You are performing a query and a DML statment (update) inside of a loop.  This is not considered bulkified and you're susceptible to hitting SOQL query and DML limits.  I've modified your class below to demonstrate how to bulkify your code to better avoid these limits.  Also, it's a best practice to not put too much logic into your execute() method.  It will be easier to test if you break the logic out of the execute() method.

It looks like your checkrun() method just creates the CRON time and schedules the class.  However, the time that you are entering is just a single time, not a regular interval, and it will be in the past by the time you try to schedule it.  

Try this:
 
global class SalesOrderSchedule implements Schedulable
{
    global static String CRON_EXP = '0 0 0 * * ? *'; // this will schedule for 12:00:00 AM every day.

    global void execute(SchedulableContext sc)
    {
        doOpportunityUpdates();
    }

    global void doOpportunityUpdates()
    {
        List<Opportunity> opps = new List<Opportunity>();
    
        for(Sales_Order__c salesorder : [select id, Name, LastModifiedDate, Quote__c, OrderNum__c from Sales_Order__c WHERE LastModifiedDate  >= LAST_N_DAYS:1])
        {
            String OppNewStage = null;
            
            if(salesorder.OrderNum__c == 'QUOTE WON') {
                OppNewStage = 'Closed Won';
            } else if(salesorder.OrderNum__c == 'QUOTE LOST') {
                OppNewStage = 'Closed Lost';
            } else {
                OppNewStage = 'Quoted';
            }
          
            // add the opportunity to a list so you are not updating them one at a time.
            // no need to query the opportunity, just populate the Id.
            Opportunity opp = new Opportunity(Id = salesorder.Quote__c, StageName = OppNewStage);
            opps.add(opp);
        }
        
        update opps;   // update the entire list in a single DML statement outside of the for-loop.
    }
}

You can schedule this class from the Schedule Apex button by going to Setup > Develop > Apex Classes > Schedule Apex > Choose a daily frequency.

You could also open the Developer Console and run an anonymous apex script like this:
 
System.schedule('Scheduled Job of Sales Order, SalesOrderSchedule.CRON_EXP, new SalesOrderSchedule());

Your test class would be this:
 
@istest
public with sharing class Test_SalesOrderSchedule
{
    @TestSetup
    static void setupData()
    {
        // You need to setup test data, i.e. Account, Opportunity, Sales Order
        Account a = new Account(Name = 'Test Account');
        insert a;

        // add any additional required fields.
        Opportunity oppOne = new Opportunity(Name = 'Test Opp 1', StageName = 'Open', CloseDate = System.today());
        insert oppOne;
       
        // add any additional required fields.
        Sales_Order__c salesOrder = new Sales_Order__c(Quote__c = oppOne.Id, OrderNum__c = 'QUOTE WON');
        insert salesOrder;
    }

    /**
     *  Test that the class is scheduled appropriately.
     *
    **/
    static testmethod void testOne()
    {
        Test.startTest();

        Id jobId = System.schedule('Sales Order Job Scheduler', SalesOrderSchedule.CRON_EXP, new SalesOrderSchedule());

        Test.stopTest();
 
        System.assert(jobId != null);  // ensure the class was scheduled.
    }

    /**
     *  Test the logic of the doOpportunityUpdates() method.
     *
    **/
    static testmethod void testTwo()
    {
        Test.startTest();

        SalesOrderSchedule so = new SalesOrderSchedule();
        so.doOpportunityUpdates();

        Test.stopTest();

        // based on the test data, the opp should be in the Closed Won stage.
        Opportunity oppOne = [select Id, StageName from Opportunity where Name = 'Test Opp 1' limit 1];
        System.assertEquals('Closed Won', oppOne.StageName);
    } 
}

Hope that helps,

Clint

All Answers

Anupama SamantroyAnupama Samantroy
Hi,

I dont see you are using the method checkrun(). Do you need this method?

Thanks 
Anupama
David ZhuDavid Zhu
I had the similar problem before, but If you break into two test methods, it should have 100% coverage.
 
@isTest
public class SalesOrderScheduleTest {
    static testMethod void myTestMethod1() {        
         test.starttest();
     SalesOrderSchedule.checkrun();
        test.stopTest();
    }
     
    static testMethod void myTestMethod2() {        
         test.starttest();
   String chron = '0 0 12 15 9 ? 2015' ;        
         system.schedule('job', chron, new SalesOrderSchedule());
         test.stopTest();
    }
}

 
ClintLeeClintLee
Hi Waqas,

You have a couple of issues with your execute method that I'd advise modifying.  You are performing a query and a DML statment (update) inside of a loop.  This is not considered bulkified and you're susceptible to hitting SOQL query and DML limits.  I've modified your class below to demonstrate how to bulkify your code to better avoid these limits.  Also, it's a best practice to not put too much logic into your execute() method.  It will be easier to test if you break the logic out of the execute() method.

It looks like your checkrun() method just creates the CRON time and schedules the class.  However, the time that you are entering is just a single time, not a regular interval, and it will be in the past by the time you try to schedule it.  

Try this:
 
global class SalesOrderSchedule implements Schedulable
{
    global static String CRON_EXP = '0 0 0 * * ? *'; // this will schedule for 12:00:00 AM every day.

    global void execute(SchedulableContext sc)
    {
        doOpportunityUpdates();
    }

    global void doOpportunityUpdates()
    {
        List<Opportunity> opps = new List<Opportunity>();
    
        for(Sales_Order__c salesorder : [select id, Name, LastModifiedDate, Quote__c, OrderNum__c from Sales_Order__c WHERE LastModifiedDate  >= LAST_N_DAYS:1])
        {
            String OppNewStage = null;
            
            if(salesorder.OrderNum__c == 'QUOTE WON') {
                OppNewStage = 'Closed Won';
            } else if(salesorder.OrderNum__c == 'QUOTE LOST') {
                OppNewStage = 'Closed Lost';
            } else {
                OppNewStage = 'Quoted';
            }
          
            // add the opportunity to a list so you are not updating them one at a time.
            // no need to query the opportunity, just populate the Id.
            Opportunity opp = new Opportunity(Id = salesorder.Quote__c, StageName = OppNewStage);
            opps.add(opp);
        }
        
        update opps;   // update the entire list in a single DML statement outside of the for-loop.
    }
}

You can schedule this class from the Schedule Apex button by going to Setup > Develop > Apex Classes > Schedule Apex > Choose a daily frequency.

You could also open the Developer Console and run an anonymous apex script like this:
 
System.schedule('Scheduled Job of Sales Order, SalesOrderSchedule.CRON_EXP, new SalesOrderSchedule());

Your test class would be this:
 
@istest
public with sharing class Test_SalesOrderSchedule
{
    @TestSetup
    static void setupData()
    {
        // You need to setup test data, i.e. Account, Opportunity, Sales Order
        Account a = new Account(Name = 'Test Account');
        insert a;

        // add any additional required fields.
        Opportunity oppOne = new Opportunity(Name = 'Test Opp 1', StageName = 'Open', CloseDate = System.today());
        insert oppOne;
       
        // add any additional required fields.
        Sales_Order__c salesOrder = new Sales_Order__c(Quote__c = oppOne.Id, OrderNum__c = 'QUOTE WON');
        insert salesOrder;
    }

    /**
     *  Test that the class is scheduled appropriately.
     *
    **/
    static testmethod void testOne()
    {
        Test.startTest();

        Id jobId = System.schedule('Sales Order Job Scheduler', SalesOrderSchedule.CRON_EXP, new SalesOrderSchedule());

        Test.stopTest();
 
        System.assert(jobId != null);  // ensure the class was scheduled.
    }

    /**
     *  Test the logic of the doOpportunityUpdates() method.
     *
    **/
    static testmethod void testTwo()
    {
        Test.startTest();

        SalesOrderSchedule so = new SalesOrderSchedule();
        so.doOpportunityUpdates();

        Test.stopTest();

        // based on the test data, the opp should be in the Closed Won stage.
        Opportunity oppOne = [select Id, StageName from Opportunity where Name = 'Test Opp 1' limit 1];
        System.assertEquals('Closed Won', oppOne.StageName);
    } 
}

Hope that helps,

Clint
This was selected as the best answer
Amit Chaudhary 8Amit Chaudhary 8
You are trying to call you scheduler class two time one by checkrun and then by below line
system.schedule('job', chron, new SalesOrderSchedule());

Just Try only checkRun Method like below
 
@isTest
public class SalesOrderScheduleTest {
    static testMethod void myTestMethod1() 
	{
		Sales_Order__c salOrder = new Sales_Order__c();
		// Add all required field here
		insert salOrder;
		
		
        test.starttest();
			SalesOrderSchedule.checkrun();
        test.stopTest();
    }
     
}
NOTE:- You can scheduler the same batch job more the one time.
Please check below post for test classess.. I hope that will help u
http://amitsalesforce.blogspot.com/search/label/Test%20Class


Please follow below salesforce Best Practice for Test Classes :-

1. Test class must start with @isTest annotation if class class version is more than 25
2. Test environment support @testVisible , @testSetUp as well
3. Unit test is to test particular piece of code working properly or not .
4. Unit test method takes no argument ,commit no data to database ,send no email ,flagged with testMethod keyword .
5. To deploy to production at-least 75% code coverage is required
6. System.debug statement are not counted as a part of apex code limit.
7. Test method and test classes are not counted as a part of code limit
9. We should not focus on the  percentage of code coverage ,we should make sure that every use case should covered including positive, negative,bulk and single record .
Single Action -To verify that the the single record produces the correct an expected result .
Bulk action -Any apex record trigger ,class or extension must be invoked for 1-200 records .
Positive behavior : Test every expected behavior occurs through every expected permutation , i,e user filled out every correctly data and not go past the limit .
Negative Testcase :-Not to add future date , Not to specify negative amount.
Restricted User :-Test whether a user with restricted access used in your code .10. Test class should be annotated with @isTest .
11 . @isTest annotation with test method  is equivalent to testMethod keyword .
12. Test method should static and no void return type .
13. Test class and method default access is private ,no matter to add access specifier .
14. classes with @isTest annotation can't be a interface or enum .
15. Test method code can't be invoked by non test request .
16. Stating with salesforce API 28.0 test method can not reside inside non test classes .
17. @Testvisible annotation to make visible private methods inside test classes.
18. Test method can not be used to test web-service call out . Please use call out mock .
19. You can't  send email from test method.
20.User, profile, organization, AsyncApexjob, Corntrigger, RecordType, ApexClass, ApexComponent ,ApexPage we can access without (seeAllData=true) .
21. SeeAllData=true will not work for API 23 version eailer .
22. Accessing static resource test records in test class e,g List<Account> accList=Test.loadData(Account,SobjectType,'ResourceName').
23. Create TestFactory class with @isTest annotation to exclude from organization code size limit .
24. @testSetup to create test records once in a method  and use in every test method in the test class .
25. We can run unit test by using Salesforce Standard UI,Force.com IDE ,Console ,API.
26. Maximum number of test classes run per 24 hour of period is  not grater of 500 or 10 multiplication of test classes of your organization.
27. As apex runs in system mode so the permission and record sharing are not taken into account . So we need to use system.runAs to enforce record sharing .
28. System.runAs will not enforce user permission or field level permission .
29. Every test to runAs count against the total number of DML issued in the process .


Please let us know if this post will help you

Thanks
Amit Chaudhary