1

I have a class which implements Schedulable interface. All it does is, query some list of records and mark them as 'COMPLETE' in the end.

Class looks like following:

global class MyScheduledClass implements Schedulable{

global void execute(SchedulableContext ctx) {

// Run a SOQL to get list of records of some criteria and mark their status as complete.

List<Database.SaveResult> lstResults = Database.update(lstResultsToUpdate, false);

logErrorsIfAny(lstResults);

}

private void logErrorsIfAny(List<Database.SaveResult> lstSaveResult){
    for(Database.SaveResult aResult: lstSaveResult){
        if(aResult.isSuccess() == false){
            for (Database.Error err : aResult.getErrors()){
                System.debug('Error while updating id: '+aResult.getId()+' Message: '+err.getMessage());
            }
        }
    }
}

In the test, I am trying to run the test method as a read-only user or an integration user which has a minimal access and does not have an access to modify any data. But, I believe the running user is just putting the job in the queue and Salesforce is running the job and due to that it is not hitting the error.

Here's how my part of test class looks like:

    Test.startTest();

    User readOnlyUser = createUser();

    System.runAs(readOnlyUser){
        try{        
            String jobId = System.schedule('ScheduledApexTest', CRON_EXP, new MyScheduledClass());
        }catch(Exception e){
            System.assert(e.getMessage().contains('Error while updating save attempt id:'));
        }
    }
    Test.stopTest();

I am not sure how to test the Database.Error piece of code in the Scheduled job.

2 Answers 2

1

Place @testvisible annotation above logerrorsifany

in test class

make a record that will fire a validation rule or throw an error

put in list

use database.upsert

pass the upsert return into your now testvisible method

basically, if you use testvisible you just have to make a record fail to upsert then you can pass it to that class. Maybe you have a simpler solution, you can throw the record against a validation rule depending what you have in your system

best of luck

2
  • Thank you for the suggestion. Though I was not able to add two records of same id in a list and also I was able to change the Id of a record and Salesforce was able to update it. I used your suggestion to update a currency field and many numbers and it threw me the exception I wanted. Commented Apr 10, 2018 at 21:26
  • my pleasure, I have modified my answer to fit with your comment. If it helped, please mark as best answer
    – Tim Willis
    Commented Apr 10, 2018 at 21:59
1

Ideally you should avoid handling exceptions in your test code. Your original scheduler class should handle any exceptions coming out of code.

In above code you are logging the errors in system debug, but you can think of logging them to an error object, that way you can look at the records of that object and see what's caused the error instead of going through debug logs.

For asynchronous apex testing, apex collects all the async calls after "Test.stopTest()" method and executes them synchronously.

So you should be able to query the original list and see if the changes took place or not. Below is the sample code I wrote for understanding purpose.

    global class MyScheduledClass implements Schedulable{

global void execute(SchedulableContext ctx) {

// Run a SOQL to get list of records of some criteria and mark their status as complete.
List<Account> lstResultsToUpdate = new List<Account>();
for(Account recAcc : [Select Id, Name, Status__c FROM Account WHERE Status__c != 'COMPLETE']){
    recAcc.Status__c = 'COMPLETE';
    lstResultsToUpdate.add(recAcc);
}

List<Database.SaveResult> lstResults = Database.update(lstResultsToUpdate, false);

logErrorsIfAny(lstResults);

}

private void logErrorsIfAny(List<Database.SaveResult> lstSaveResult){
    for(Database.SaveResult aResult: lstSaveResult){
        if(aResult.isSuccess() == false){
            for (Database.Error err : aResult.getErrors()){
                System.debug('Error while updating id: '+aResult.getId()+' Message: '+err.getMessage());
            }
        }
    }
}
}

Sample test class

@isTest
private class MyScheduleTest{

    static testMethod void runErrorCase(){


        User readOnlyUser = [Select Id FROM User WHERE IsActive = true LIMIT 1];//User your logic to query users
        Test.startTest();
        System.runAs(readOnlyUser){
            String jobId = System.schedule('ScheduledApexTest', CRON_EXP, new MyScheduledClass());
        }
        Test.stopTest();

        //Query Accounts to check if Status got updated assuming 10 were supposed to be updated. 
        System.assertNotEquals([Select COUNT() FROM Account WHERE Status__c = 'COMPLETE'], 10);
    }
}
1
  • Thank you for the response. System.debug was easier for debugging but yes, we do have a custom error logging mechanism. What I am looking for is, how can I force the code to go into the for loop of Database.Error. Without that I am getting 93% code coverage but I want to get full 100%. Commented Apr 10, 2018 at 20:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .