mcpField
G
generating-apex-test

generating-apex-test

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

关于 generating-apex-test

用于生成 Salesforce Apex 测试类并执行有步骤的测试修复与覆盖率分析,面向编写和维护 Apex 测试的开发者。它强调数据隔离、精确断言、异步测试及正向、负向和批量场景,并规定测试文件交付格式。

主要功能

  1. 01按单一行为组织测试方法
  2. 02通过 TestDataFactory 隔离数据
  3. 03使用 Assert 编写精确断言
  4. 04模拟外部调用并覆盖错误路径
  5. 05运行测试并分析覆盖率
  6. 06最多三轮的测试修复流程

适用场景

  1. 01为 Apex 类生成测试类及元数据文件
  2. 02检查触发器的 251 条以上记录批量场景
  3. 03测试队列、批处理和定时异步逻辑
  4. 04根据测试失败和覆盖率定位问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill generating-apex-test

安装后在 Claude Code 中输入/generating-apex-test确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CREDITS.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 步 — 分析结果

重点关注:

  • 失败的方法 — 异常类型和堆栈跟踪
  • 未覆盖的行和覆盖率较低的区域
  • 失败是否表明测试数据有误、断言不可靠,或生产逻辑已损坏
第 5 步 — 修复循环

测试失败时,执行有纪律的修复循环(最多 3 次迭代 — 如果仍然失败,则停止并报告根本原因):

  1. 阅读失败的测试类和被测类
  2. 根据错误消息和堆栈跟踪确定根本原因
  3. 应用修复 — 对于测试端问题,调整测试数据或断言;生产代码问题交由 generating-apex 技能处理
  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 mock
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 mocking、StubProvider、SOSL、Email、Platform Events
references/async-testing.mdBatch、Queueable、Future、Scheduled 作业测试

评论

支持 Markdown