Salesforce部署Apex触发器沙箱覆盖率57%生产报0%问题排查
Apex触发器部署报0%代码覆盖率问题修复
你遇到的部署报错核心是测试类本身运行失败、触发器逻辑存在缺陷,沙箱显示的57%覆盖率是历史测试缓存导致的无效数据,生产部署时会重新执行所有测试,测试失败的类对应代码覆盖率直接计为0。具体问题点和修复方案如下:
- 触发器逻辑缺陷:触发器仅绑定了
before insert时机,但代码里直接调用Trigger.oldMap.get(leadObj.Id),insert场景下Trigger.oldMap本身为null,执行到这行直接抛空指针异常;且你的逻辑是判断字段变更后计算值,字段变更只存在于update场景,原触发时机配置错误。 - 测试类致命错误:你在测试方法里new了Lead对象后,没有先执行insert操作就直接调用update,DML操作要求update的记录必须存在有效Id,这行代码会直接抛DML异常,整个测试方法执行失败,根本不会覆盖到触发器代码。
- 测试类写法不规范:手动给新建的Lead对象设置
Elapsed_Time__c这类触发器计算的字段是无效操作,且整个测试方法没有任何断言逻辑,不符合Salesforce测试类的强制规范。
第一步:修正触发器代码
调整触发时机为before insert, before update,增加场景判断避免空指针,覆盖插入时首次赋值、更新时字段变更两个场景:
trigger Business_Hours on Lead (before insert, before update) { // 查询默认营业时间配置 BusinessHours defaultBH = [SELECT Id FROM BusinessHours WHERE IsDefault = true LIMIT 1]; if(defaultBH != null){ for(Lead leadObj : Trigger.new){ Boolean needCalc = false; // 插入场景:只要Call_Made__c有值就计算 if(Trigger.isInsert){ needCalc = leadObj.Call_Made__c != null; } // 更新场景:Call_Made__c有值且发生变更才计算 if(Trigger.isUpdate){ needCalc = leadObj.Call_Made__c != null && Trigger.oldMap.get(leadObj.Id).Call_Made__c != leadObj.Call_Made__c; } if(needCalc){ Decimal diffMs = BusinessHours.diff(defaultBH.Id, leadObj.CreatedDate, leadObj.Call_Made__c); // 毫秒转分钟,保留4位小数 leadObj.Elapsed_Time__c = (diffMs / 60000).setScale(4); } } } }
第二步:重写合规测试类
测试类里自行构造测试用营业时间数据,避免依赖不同环境的现有配置,分别覆盖插入、更新两个触发场景,增加断言验证逻辑正确性:
@isTest public class Business_Hours_Test { @testMethod static void testInsertLeadWithCallMade() { // 构造测试用默认营业时间,不依赖组织现有配置 BusinessHours testBH = new BusinessHours( Name = 'Test BH', IsDefault = true, MondayStartTime = Time.newInstance(9,0,0,0), MondayEndTime = Time.newInstance(18,0,0,0), TuesdayStartTime = Time.newInstance(9,0,0,0), TuesdayEndTime = Time.newInstance(18,0,0,0), WednesdayStartTime = Time.newInstance(9,0,0,0), WednesdayEndTime = Time.newInstance(18,0,0,0), ThursdayStartTime = Time.newInstance(9,0,0,0), ThursdayEndTime = Time.newInstance(18,0,0,0), FridayStartTime = Time.newInstance(9,0,0,0), FridayEndTime = Time.newInstance(18,0,0,0) ); insert testBH; // 构造测试Lead,设置Call_Made时间为创建后1小时(工作时间内) Lead testLead = new Lead( LastName = 'Test', FirstName = 'Test', Company = 'ABC', Brand__c = 'Rain' ); DateTime createTime = DateTime.newInstance(2024, 1, 2, 9, 0, 0); // 2024年1月2日周二9点 testLead.CreatedDate = createTime; testLead.Call_Made__c = createTime.addHours(1); // 10点,间隔60分钟 Test.startTest(); insert testLead; Test.stopTest(); // 验证计算结果正确 Lead res = [SELECT Elapsed_Time__c FROM Lead WHERE Id = :testLead.Id]; System.assertEquals(60, res.Elapsed_Time__c.setScale(0), '插入场景时间计算错误'); } @testMethod static void testUpdateLeadCallMade() { // 构造测试营业时间 BusinessHours testBH = new BusinessHours( Name = 'Test BH2', IsDefault = true, MondayStartTime = Time.newInstance(9,0,0,0), MondayEndTime = Time.newInstance(18,0,0,0), TuesdayStartTime = Time.newInstance(9,0,0,0), TuesdayEndTime = Time.newInstance(18,0,0,0), WednesdayStartTime = Time.newInstance(9,0,0,0), WednesdayEndTime = Time.newInstance(18,0,0,0), ThursdayStartTime = Time.newInstance(9,0,0,0), ThursdayEndTime = Time.newInstance(18,0,0,0), FridayStartTime = Time.newInstance(9,0,0,0), FridayEndTime = Time.newInstance(18,0,0,0) ); insert testBH; // 先插入不带Call_Made值的Lead Lead testLead = new Lead(LastName='Test2', FirstName='Test2', Company='ABC2', Brand__c='Rain'); insert testLead; DateTime createTime = testLead.CreatedDate; // 更新Call_Made字段触发更新逻辑 testLead.Call_Made__c = createTime.addMinutes(30); Test.startTest(); update testLead; Test.stopTest(); // 验证结果 Lead res = [SELECT Elapsed_Time__c FROM Lead WHERE Id = :testLead.Id]; System.assertEquals(30, res.Elapsed_Time__c.setScale(0), '更新场景时间计算错误'); } }
部署前检查
- 在沙箱中清除旧的测试缓存,重新运行Business_Hours_Test类,确认触发器代码覆盖率达到100%,所有测试用例通过
- 部署时选择运行指定测试,勾选Business_Hours_Test,避免其他无关测试类失败影响本次部署
内容的提问来源于stack exchange,提问作者lisa haigy
相关产品推荐
相关产品推荐

