function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Roy LuoRoy Luo 

Gotcha: unit test codes with System.scheduleBatch

In unit tests, System.scheduleBatch only schdules the job and the batchable won't execute. You should see the scheduled job with this query:
SELECT Count() FROM CronTrigger WHERE CronJobDetail.Name='...'
But that is very much it. System.assert on your expected results from the batchable will always fail. 

A picture Code is worth a thousand words, here are the batchable and unit tests I have:
 
public class MyBatchable implements Database.Batchable<SObject>
{
    String soqlQuery {get;set;}
    String AccountNumber {get;set;}
    
    public MyBatchable(String acctNumber)
    {
        soqlQuery = 'SELECT Id, Name, AccountNumber FROM Account';
        AccountNumber = acctNumber;
    }
    
    public Database.QueryLocator start(Database.BatchableContext BC)
    {          
        return Database.getQueryLocator(soqlQuery);
    }
 
    public  void execute(Database.BatchableContext BC, List<SObject> scope)
    {        
        List<Account> accounts = (List<Account>)scope;
        for(Account a: accounts)
        {
            a.AccountNumber = AccountNumber ;
        }
        update accounts;
    }
 
    public void finish(Database.BatchableContext BC)
    {   
    }
}
 
@isTest
private class MyBatchableTest
{
    static testmethod void DatabaseExcuteBatch_Test()
    {
        Test.startTest();
        SetupTestData(100);
        String acctNumber = '987654321';
        
        Database.executeBatch(new MyBatchable(acctNumber));
        
        Test.stopTest();
        Integer count = [SELECT COUNT() FROM Account WHERE AccountNumber=:acctNumber];
        System.assertEquals(100, count);
    }
    
    static testmethod void ScheduleBatch_Test()
    {
        Test.startTest();
        SetupTestData(100);
        String acctNumber = '987654321';  
              
        System.scheduleBatch(new MyBatchable(acctNumber), 'MyBatchable', 1);
        
        Test.stopTest();
        Integer count = [SELECT COUNT() FROM Account WHERE AccountNumber=:acctNumber];
        System.assertEquals(100, count);
    }   
    
    static void SetupTestData(Integer acctCount)
    {       
        List<Account> accts = new List<Account>();
        for(Integer i=0; i<acctCount; i++)
        {
            accts.add(new Account(Name=getGuid()));
        }
        insert accts;
    }
    
    static String getGuid()
    {
        String v = EncodingUtil.convertToHex(Crypto.generateDigest('SHA1', Blob.valueOf(EncodingUtil.ConvertTohex(Crypto.GenerateAESKey(128))))).toUpperCase(); 
        return (v.subString(0,8) + '-' + v.subString(8,12) + '-' + v.subString(12,16) + '-' + v.subString(16,20) + '-' + v.subString(20,32)).ToUpperCase();
    }
}


DatabaseExcuteBatch_Test calling Database.executeBatch will pass while ScheduleBatch_Test (with System.scheduleBatch) fails.

So unit testing codes with System.scheduleBatch would require two pieces, one to test the job is scheduled and another one to test data results from your batchable. 

 
Roy LuoRoy Luo
To test if the job is schduled, the unit test would be like this:
 
static testmethod void ScheduleBatch_Test()
    {
        Test.startTest();
        SetupTestData(100);
        String acctNumber = '987654321';  
              
        System.scheduleBatch(new MyBatchable(acctNumber), 'MyBatchable', 1);
        
        Test.stopTest();
        Integer count = [SELECT Count() FROM CronTrigger WHERE CronJobDetail.Name='MyBatchable'];
        System.assertEquals(1, count);
    }