mcpField
P
platform-apex-test-generate

platform-apex-test-generate

github.com/forcedotcom测试与质量1.1k7.1k 次安装1 天前更新
代码仓库

关于 platform-apex-test-generate

用于生成 Salesforce Apex 测试类,并结合覆盖率分析迭代修复测试失败。适合需要为同步或异步代码编写隔离且有明确断言的测试的开发者。

主要功能

  1. 01按行为拆分测试方法
  2. 02通过工厂隔离测试数据
  3. 03断言与模拟模式
  4. 04异步及批量测试指导
  5. 05覆盖率分析与修复循环

适用场景

  1. 01为 Apex 类编写测试
  2. 02覆盖触发器批量行为
  3. 03测试调用和异步任务
  4. 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.cls
test-data-factory-template.cls
references/
assertion-patterns.md
async-testing.md
mocking-patterns.md
test-data-factory.md

详细介绍

生成 Apex 测试

生成可用于生产环境的 Apex 测试类,并通过覆盖率分析执行严谨的测试修复循环。

核心原则

  1. 每个方法只测试一种行为 — 每个测试方法验证一个场景。将正向、负向和批量测试分开。绝不要在同一个方法中组合相关但不同的输入(例如 null 和空值)——应创建 _NullInput_ 和 _EmptyInput_ 作为独立的测试方法
  2. 测试批量处理能力 — 使用 251 条或更多记录进行测试,以跨越 200 条记录的触发器批次边界。**Batch Apex 例外:**在测试上下文中,只会调用一次 execute(),因此应设置 batchSize >= testRecordCount。请参阅 references/async-testing.md
  3. 隔离测试数据 — 每个 @TestSetup 都必须委托 TestDataFactory 类创建记录。如果不存在此类,先创建它。绝不要在 @TestSetup 中内联创建记录列表。绝不要依赖组织数据(SeeAllData=false)或硬编码 ID。关于重复规则处理,请参阅 references/test-data-factory.md
  4. 有意义地断言 — 使用根据测试数据设置计算出的精确预期值。如果值是确定的,绝不要使用范围断言或近似计数。始终包含失败消息。请参阅 references/assertion-patterns.md
  5. 只使用 Assert 类 — 使用 Assert.areEqual、Assert.isTrue、Assert.fail 等。绝不要使用旧版 System.assert、System.assertEquals 或 System.assertNotEquals
  6. 模拟外部边界 — 对调用外部服务使用 HttpCalloutMock,对 SOSL 使用 Test.setFixedSearchResults,对数据库隔离使用 DML 模拟类。通过构造函数注入来设计可测试性。请参阅 references/mocking-patterns.md
  7. 测试负向路径 — 验证错误处理和异常场景,而不只是成功路径
  8. 使用 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 — 生成测试类

遵循资源模板和参考文档中的结构、命名约定和模式。

**必需 — 文件交付物:**每个测试类都要创建以下两个文件:

  1. {ClassName}Test.cls — 测试类(以 assets/test-class-template.cls 为起点)
  2. {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 次 — 如果仍然失败,则停止并报告根本原因):

  1. 阅读失败的测试类和被测类
  2. 根据错误消息和堆栈跟踪确定根本原因
  3. 应用修复 — 对于测试端问题,调整测试数据或断言;对于生产代码问题,交由 platform-apex-generate skill 处理
  4. 在更大范围的回归测试之前,重新运行针对性测试
  5. 重复上述步骤,直到所有测试通过、达到迭代次数上限,或根本原因需要进行设计更改
步骤 6 — 验证覆盖率
级别覆盖率用途
生产部署最低 75%Salesforce 的要求
推荐90%+最佳实践目标
关键路径100%业务关键代码

覆盖所有路径:正向、负向/异常、大批量(251+ 条记录)、Callout/异步。

按组件确定测试内容

组件关键测试场景
Trigger大批量插入/更新/删除、递归保护、字段更改检测
Service有效/无效输入、大批量操作、异常处理
Controller页面加载、操作方法、视图状态
Batchstart/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 EventTest.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.mdTestDataFactory 模式、字段覆盖、重复规则处理
references/assertion-patterns.md断言最佳实践、反模式、常见陷阱
references/mocking-patterns.mdHttpCalloutMock、DML 模拟、StubProvider、SOSL、Email、Platform Events
references/async-testing.mdBatch、Queueable、Future、Scheduled 作业测试

评论

支持 Markdown