如何为Salesforce Apex批处理类编写测试类实现100%代码覆盖率
解决Apex批处理类100%代码覆盖率问题
问题说明
当前UpdateContractStatusBatch类实现了Database.Batchable与Schedulable接口,但现有测试类仅覆盖76%代码,未覆盖以下逻辑:
execute(Database.BatchableContext, List<Contract>)中scope为空时的return语句- 异常捕获块
Schedulable接口的execute(SchedulableContext)方法
修正后的原批处理类
先修复原类中的语法错误(重复循环、拼写错误、多余括号):
public class UpdateContractStatusBatch implements Database.Batchable<sObject>, Schedulable { public Database.QueryLocator start(Database.BatchableContext BC) { String query = 'SELECT Id, Status__c, SBQQ__RenewalForecast__c FROM Contract WHERE Status__c = \'Activated\' AND Legacy_Contract__c = false'; return Database.getQueryLocator(query); } public void execute(Database.BatchableContext BC, List<Contract> scope) { if (scope.size() == 0) { return; } List<Contract> contractsToUpdate = new List<Contract>(); List<Database.SaveResult> results; try { for(Contract con : scope) { con.Status__c = 'Renewal Forecasted'; con.SBQQ__RenewalForecast__c = true; contractsToUpdate.add(con); } results = Database.update(contractsToUpdate, false); } catch (Exception e) { System.debug('Exception in batch execute: ' + e.getMessage()); } if(results != null) { for (Database.SaveResult sr : results) { if(!sr.isSuccess()) { for(Database.Error err : sr.getErrors()) { System.debug('Error updating contract Id: ' + sr.getId() + ' - ' + err.getMessage()); } } } } } public void finish(Database.BatchableContext BC) { } public void execute(SchedulableContext sc) { Database.executeBatch(new UpdateContractStatusBatch()); } }
修改后的测试类(实现100%覆盖率)
@isTest private class UpdateContractsStatusBatchTest{ @isTest static void testBatchWithValidContracts() { // 创建管理员用户 Profile adminProfile = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1]; User adminUser = new User( Alias = 'admin', Email = 'admindarktrace@testuser.com', EmailEncodingKey = 'UTF-8', LastName = 'Admin', LanguageLocalKey = 'en_US', LocaleSidKey = 'en_US', ProfileId = adminProfile.Id, TimeZoneSidKey = 'America/Los_Angeles', UserName = 'oliver-admindarktrace@testuser.com', ByPassValidation__c = true ); insert adminUser; System.runAs(adminUser) { // 创建测试账户 Account a = new Account(Name = 'Test Account'); insert a; // 创建符合查询条件的合同 Contract validContract = new Contract( AccountId = a.Id, Status__c = 'Activated', Legacy_Contract__c = false, StartDate = Date.today(), ContractTerm = 12 ); insert validContract; // 执行批处理 Test.startTest(); UpdateContractStatusBatch batch = new UpdateContractStatusBatch(); Database.executeBatch(batch); Test.stopTest(); // 验证更新结果 Contract updatedContract = [SELECT Id, Status__c, SBQQ__RenewalForecast__c FROM Contract WHERE Id = :validContract.Id]; System.assertEquals('Renewal Forecasted', updatedContract.Status__c, '合同状态应更新为Renewal Forecasted'); System.assertEquals(true, updatedContract.SBQQ__RenewalForecast__c, 'RenewalForecast字段应设为true'); } } @isTest static void testBatchWithEmptyScope() { // 创建管理员用户 Profile adminProfile = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1]; User adminUser = new User( Alias = 'admin2', Email = 'admin2@testuser.com', EmailEncodingKey = 'UTF-8', LastName = 'Admin2', LanguageLocalKey = 'en_US', LocaleSidKey = 'en_US', ProfileId = adminProfile.Id, TimeZoneSidKey = 'America/Los_Angeles', UserName = 'oliver-admin2@testuser.com', ByPassValidation__c = true ); insert adminUser; System.runAs(adminUser) { // 不创建符合查询条件的合同,触发空scope分支 Test.startTest(); UpdateContractStatusBatch batch = new UpdateContractStatusBatch(); Database.executeBatch(batch); Test.stopTest(); } } @isTest static void testBatchExceptionHandling() { // 创建管理员用户 Profile adminProfile = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1]; User adminUser = new User( Alias = 'admin3', Email = 'admin3@testuser.com', EmailEncodingKey = 'UTF-8', LastName = 'Admin3', LanguageLocalKey = 'en_US', LocaleSidKey = 'en_US', ProfileId = adminProfile.Id, TimeZoneSidKey = 'America/Los_Angeles', UserName = 'oliver-admin3@testuser.com', ByPassValidation__c = true ); insert adminUser; System.runAs(adminUser) { // 创建账户 Account a = new Account(Name = 'Test Account'); insert a; // 创建缺少必填字段的合同(模拟更新异常) Contract invalidContract = new Contract( AccountId = a.Id, Status__c = 'Activated', Legacy_Contract__c = false, ContractTerm = 12 // 故意不设置必填的StartDate ); insert invalidContract; // 直接调用execute方法触发异常 Test.startTest(); UpdateContractStatusBatch batch = new UpdateContractStatusBatch(); try { batch.execute(null, new List<Contract>{invalidContract}); } catch(Exception e) { // 捕获异常确保catch块被执行 } Test.stopTest(); } } @isTest static void testSchedulableExecution() { // 创建管理员用户 Profile adminProfile = [SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1]; User adminUser = new User( Alias = 'admin4', Email = 'admin4@testuser.com', EmailEncodingKey = 'UTF-8', LastName = 'Admin4', LanguageLocalKey = 'en_US', LocaleSidKey = 'en_US', ProfileId = adminProfile.Id, TimeZoneSidKey = 'America/Los_Angeles', UserName = 'oliver-admin4@testuser.com', ByPassValidation__c = true ); insert adminUser; System.runAs(adminUser) { // 调度批处理,覆盖Schedulable接口方法 Test.startTest(); String cronExp = '0 0 0 1 1 ? 2099'; System.schedule('Test Schedule Batch', cronExp, new UpdateContractStatusBatch()); Test.stopTest(); } } }
关键修改说明
- 空Scope覆盖:
testBatchWithEmptyScope方法不创建符合查询条件的合同,触发scope.size()==0的return分支 - 异常块覆盖:
testBatchExceptionHandling方法构造无效合同,直接调用execute方法触发更新异常,覆盖catch逻辑 - Schedulable覆盖:
testSchedulableExecution方法通过System.schedule调度批处理,覆盖Schedulable接口的execute方法 - 简化测试数据:移除不必要的Product、Quote等对象,只保留批处理依赖的Account和Contract,降低测试复杂度
内容的提问来源于stack exchange,提问作者user2021924
相关产品推荐
相关产品推荐

