关于 platform-apex-test-generate
用于生成 Salesforce Apex 测试类,并结合覆盖率分析迭代修复测试失败。适合需要为同步或异步代码编写隔离且有明确断言的测试的开发者。
主要功能
- 01按行为拆分测试方法
- 02通过工厂隔离测试数据
- 03断言与模拟模式
- 04异步及批量测试指导
- 05覆盖率分析与修复循环
适用场景
- 01为 Apex 类编写测试
- 02覆盖触发器批量行为
- 03测试调用和异步任务
- 04分析失败并弥补覆盖率缺口
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill platform-apex-test-generate安装后在 Claude Code 中输入/platform-apex-test-generate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/test-class-template.clstest-data-factory-template.clsreferences/assertion-patterns.mdasync-testing.mdmocking-patterns.mdtest-data-factory.md详细介绍
生成 Apex 测试
生成可用于生产环境的 Apex 测试类,并通过覆盖率分析执行严谨的测试修复循环。
核心原则
- 每个方法只测试一种行为 — 每个测试方法验证一个场景。将正向、负向和批量测试分开。绝不要在同一个方法中组合相关但不同的输入(例如 null 和空值)——应创建
_NullInput_和_EmptyInput_作为独立的测试方法 - 测试批量处理能力 — 使用 251 条或更多记录进行测试,以跨越 200 条记录的触发器批次边界。**Batch Apex 例外:**在测试上下文中,只会调用一次
execute(),因此应设置batchSize >= testRecordCount。请参阅 references/async-testing.md - 隔离测试数据 — 每个
@TestSetup都必须委托TestDataFactory类创建记录。如果不存在此类,先创建它。绝不要在@TestSetup中内联创建记录列表。绝不要依赖组织数据(SeeAllData=false)或硬编码 ID。关于重复规则处理,请参阅 references/test-data-factory.md - 有意义地断言 — 使用根据测试数据设置计算出的精确预期值。如果值是确定的,绝不要使用范围断言或近似计数。始终包含失败消息。请参阅 references/assertion-patterns.md
- 只使用
Assert类 — 使用Assert.areEqual、Assert.isTrue、Assert.fail等。绝不要使用旧版System.assert、System.assertEquals或System.assertNotEquals - 模拟外部边界 — 对调用外部服务使用
HttpCalloutMock,对 SOSL 使用Test.setFixedSearchResults,对数据库隔离使用 DML 模拟类。通过构造函数注入来设计可测试性。请参阅 references/mocking-patterns.md - 测试负向路径 — 验证错误处理和异常场景,而不只是成功路径
- 使用 start/stop 包装 — 配对使用
Test.startTest()和Test.stopTest(),以重置治理限制并强制执行异步操作
Test.startTest() / Test.stopTest()
始终使用 Test.startTest() / Test.stopTest() 包装被测代码:
- 重置治理限制,使测试只衡量被测代码
- 同步执行异步操作(queueables、batch、future 方法)
- 立即触发计划作业
测试代码反模式
| 反模式 | 修复方法 |
|---|---|
| 在循环中执行 SOQL/DML | 在循环前查询一次;使用 Map<Id, SObject> 进行查找 |
| 断言中使用魔法数字 | 根据设置常量推导预期值 |
| 臃肿的测试类(>500 行) | 按行为领域拆分为多个测试类 |
| 过长的测试方法(>30 行) | 将 Given/When/Then 提取为辅助方法 |
捕获通用的 Exception | 捕获特定的预期类型(例如 DmlException) |
工作流
步骤 1 — 收集上下文
生成或修复测试前,先确定:
- 要测试的目标生产类
- 现有测试类、测试数据工厂和设置辅助工具
- 所需的测试范围(单个类、特定方法、测试套件或本地测试)
- 覆盖率阈值(部署最低 75%,建议 90% 以上)
- 在组织中运行测试时使用的组织别名
步骤 2 — 生成测试类
遵循资源模板和参考文档中的结构、命名约定和模式。
**必需 — 文件交付物:**每个测试类都要创建以下两个文件:
{ClassName}Test.cls— 测试类(以 assets/test-class-template.cls 为起点){ClassName}Test.cls-meta.xml— 元数据文件:
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<status>Active</status>
</ApexClass>
如果项目中不存在 TestDataFactory,请使用 assets/test-data-factory-template.cls 创建 TestDataFactory.cls + TestDataFactory.cls-meta.xml。
@TestSetup 示例
@TestSetup
static void setupTestData() {
List<Account> accounts = TestDataFactory.createAccounts(251, true);
}
测试方法结构
使用 Given/When/Then:
@isTest
static void shouldUpdateStatus_WhenValidInput() {
// Given
List<Account> accounts = [SELECT Id FROM Account];
// When
Test.startTest();
MyService.processAccounts(accounts);
Test.stopTest();
// Then
List<Account> updated = [SELECT Id, Status__c FROM Account];
Assert.areEqual(251, updated.size(), 'All accounts should be processed');
}
负向测试 — 异常模式
使用 try/catch 和 Assert.fail 验证预期异常:
@isTest
static void shouldThrowException_WhenInvalidInput() {
// Given
List<Account> emptyList = new List<Account>();
// When/Then
Test.startTest();
try {
MyService.processAccounts(emptyList);
Assert.fail('Expected MyCustomException to be thrown');
} catch (MyCustomException e) {
Assert.isTrue(e.getMessage().contains('cannot be empty'),
'Exception message should indicate empty input');
}
Test.stopTest();
}
命名约定
should[ExpectedResult]_When[Scenario]:shouldSendNotification_WhenOpportunityClosedWon[SubjectOrAction]_[Scenario]_[ExpectedResult]:AccountUpdate_ChangeName_Success
步骤 3 — 运行测试
调试时先从小范围开始;修复稳定后再扩大范围。
# Single test class
sf apex run test --class-names MyServiceTest --result-format human --code-coverage --target-org <alias>
# Specific test methods
sf apex run test --tests MyServiceTest.shouldUpdateStatus_WhenValidInput --result-format human --target-org <alias>
# All local tests
sf apex run test --test-level RunLocalTests --result-format human --code-coverage --target-org <alias>
步骤 4 — 分析结果
重点关注:
- 失败的方法 — 异常类型和堆栈跟踪
- 未覆盖的代码行和覆盖率较低的区域
- 失败是否表明测试数据有误、断言不够稳健,或生产逻辑存在缺陷
- 当类元数据从 66.0 及以下版本跨越到 67.0 及以上版本时,检查显式共享设置,按操作确定使用用户模式还是系统模式,在测试用户或权限集中授予所需的 CRUD/FLS 权限,并重新运行受影响的测试
步骤 5 — 修复循环
测试失败时,按规范执行修复循环(最多 3 次 — 如果仍然失败,则停止并报告根本原因):
- 阅读失败的测试类和被测类
- 根据错误消息和堆栈跟踪确定根本原因
- 应用修复 — 对于测试端问题,调整测试数据或断言;对于生产代码问题,交由
platform-apex-generateskill 处理 - 在更大范围的回归测试之前,重新运行针对性测试
- 重复上述步骤,直到所有测试通过、达到迭代次数上限,或根本原因需要进行设计更改
步骤 6 — 验证覆盖率
| 级别 | 覆盖率 | 用途 |
|---|---|---|
| 生产部署 | 最低 75% | Salesforce 的要求 |
| 推荐 | 90%+ | 最佳实践目标 |
| 关键路径 | 100% | 业务关键代码 |
覆盖所有路径:正向、负向/异常、大批量(251+ 条记录)、Callout/异步。
按组件确定测试内容
| 组件 | 关键测试场景 |
|---|---|
| Trigger | 大批量插入/更新/删除、递归保护、字段更改检测 |
| Service | 有效/无效输入、大批量操作、异常处理 |
| Controller | 页面加载、操作方法、视图状态 |
| Batch | start/execute/finish、范围匹配(批次大小 >= 记录数)、Database.Stateful 跟踪、错误处理、链式调用(使用单独的方法 — finish() 调用 Database.executeBatch() 会抛出 UnexpectedException) |
| Queueable | 链式调用(测试中仅第一个作业会运行)、批量化、错误处理、在 Test.startTest() 之前设置 Callout 模拟 |
| Callout | 成功响应、错误响应、超时 |
| Selector | 有效/null/空输入、大批量(251+)、字段填充、排序顺序、通过 System.runAs 使用 WITH USER_MODE |
| Scheduled | 通过 execute(null) 直接执行、通过 CronTrigger 查询验证 CRON 注册 |
| Platform Event | Test.enableChangeDataCapture()、Test.getEventBus().deliver()、验证订阅者的副作用 |
输出要求
每个测试类的交付物:
{ClassName}Test.cls+{ClassName}Test.cls-meta.xml(与被测类的 API 版本一致;默认值为66.0)TestDataFactory.cls+TestDataFactory.cls-meta.xml(如果尚不存在)
参考文件
需要详细模式时按需加载:
| 参考文件 | 使用场景 |
|---|---|
| references/test-data-factory.md | TestDataFactory 模式、字段覆盖、重复规则处理 |
| references/assertion-patterns.md | 断言最佳实践、反模式、常见陷阱 |
| references/mocking-patterns.md | HttpCalloutMock、DML 模拟、StubProvider、SOSL、Email、Platform Events |
| references/async-testing.md | Batch、Queueable、Future、Scheduled 作业测试 |
评论
支持 Markdown