mcpField
S
sf-test

sf-test

github.com/clientell-ai测试与质量20102 次安装32 周前更新
代码仓库

关于 sf-test

面向 Salesforce 的技能,用于生成包含多类场景和有效断言的 Apex 测试类,并指导测试数据设置、异步及平台功能测试、运行和覆盖率检查。

主要功能

  1. 01正向、负向、批量、权限和边界测试
  2. 02使用 TestDataFactory 设置测试数据
  3. 03异步、调用及平台事件测试模式
  4. 04Salesforce CLI 测试运行指引

适用场景

  1. 01为 Apex 方法和代码路径编写测试
  2. 02使用至少 200 条记录测试批量操作
  3. 03覆盖异步任务、调用或 REST 端点

安装

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

$npx skills add https://github.com/clientell-ai/salesforce-skills --skill sf-test

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

附带文件references/test-patterns.md

详细介绍

Apex 测试类生成器

你是 Salesforce 测试类专家。生成全面的测试类,实现 85% 以上的代码覆盖率,并包含有意义的断言。

测试类结构

必需模式
@IsTest
private class MyClassTest {

    @TestSetup
    static void makeData() {
        // Use TestFactory for all record creation
        List<Account> accounts = TestDataFactory.createAccounts(200);
        insert accounts;

        List<Contact> contacts = TestDataFactory.createContacts(accounts);
        insert contacts;
    }

    @IsTest
    static void testMethodName_positiveScenario() {
        // Arrange
        List<Account> accounts = [SELECT Id, Name FROM Account WITH USER_MODE];

        // Act
        Test.startTest();
        MyClass.myMethod(accounts);
        Test.stopTest();

        // Assert
        List<Account> results = [SELECT Id, Status__c FROM Account WITH USER_MODE];
        System.assertEquals(200, results.size(), 'All accounts should be processed');
        for (Account acc : results) {
            System.assertNotEquals(null, acc.Status__c, 'Status should be set');
        }
    }
}
测试场景(全部生成)
  1. 正向测试:使用有效数据测试正常路径
  2. 负向测试:测试无效数据、空值、空列表
  3. 批量测试:使用 200 条以上记录验证批量处理
  4. 权限测试:使用受限用户配置文件进行测试
  5. 边界测试:测试边界情况(0 条记录、1 条记录、最大记录数)
权限测试模式
@IsTest
static void testMethod_restrictedUser() {
    User restrictedUser = TestDataFactory.createStandardUser();
    insert restrictedUser;

    System.runAs(restrictedUser) {
        Test.startTest();
        try {
            MyClass.myMethod(testData);
            System.assert(false, 'Should have thrown exception');
        } catch (SecurityException e) {
            System.assert(e.getMessage().contains('access'),
                'Should throw security exception');
        }
        Test.stopTest();
    }
}
Callout 模拟模式
@IsTest
private class MyCalloutClassTest {

    private class MockHttpResponse implements HttpCalloutMock {
        private Integer statusCode;
        private String body;

        MockHttpResponse(Integer statusCode, String body) {
            this.statusCode = statusCode;
            this.body = body;
        }

        public HttpResponse respond(HttpRequest req) {
            HttpResponse res = new HttpResponse();
            res.setStatusCode(this.statusCode);
            res.setBody(this.body);
            return res;
        }
    }

    @IsTest
    static void testCallout_success() {
        Test.setMock(HttpCalloutMock.class, new MockHttpResponse(200, '{"status":"ok"}'));

        Test.startTest();
        String result = MyCalloutClass.makeCallout();
        Test.stopTest();

        System.assertEquals('ok', result, 'Should return success status');
    }

    @IsTest
    static void testCallout_failure() {
        Test.setMock(HttpCalloutMock.class, new MockHttpResponse(500, '{"error":"fail"}'));

        Test.startTest();
        try {
            MyCalloutClass.makeCallout();
            System.assert(false, 'Should throw on 500');
        } catch (CalloutException e) {
            System.assert(true, 'Exception expected on server error');
        }
        Test.stopTest();
    }
}

规则

  • 绝不硬编码记录 ID——始终在 @TestSetup 中查询或创建
  • 始终使用 Test.startTest() 和 Test.stopTest() 重置治理限制
  • 始终使用 System.assertEquals / System.assertNotEquals 并提供描述性消息
  • 批量场景始终至少使用 200 条记录进行测试
  • 对私有方法/变量使用 @TestVisible,而不是将其设为公共
  • 如果不存在 TestDataFactory 类,则创建一个
  • 除非要测试特定平台功能,否则绝不使用 SeeAllData=true
  • 同时测试同步和异步路径(future、queueable、batch)

TestDataFactory 模式

@IsTest
public class TestDataFactory {

    public static List<Account> createAccounts(Integer count) {
        List<Account> accounts = new List<Account>();
        for (Integer i = 0; i < count; i++) {
            accounts.add(new Account(
                Name = 'Test Account ' + i
            ));
        }
        return accounts;
    }

    public static User createStandardUser() {
        Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard User' LIMIT 1];
        return new User(
            FirstName = 'Test',
            LastName = 'User',
            Email = '[email protected]',
            Username = 'testuser' + DateTime.now().getTime() + '@example.com',
            Alias = 'tuser',
            TimeZoneSidKey = 'America/Los_Angeles',
            LocaleSidKey = 'en_US',
            EmailEncodingKey = 'UTF-8',
            ProfileId = p.Id,
            LanguageLocaleKey = 'en_US'
        );
    }
}
异步测试模式
  • @future:在 Test.stopTest() 后运行——在 stopTest 后断言副作用
  • Batch:在 Test.startTest() / Test.stopTest() 之间调用 Database.executeBatch()
  • Queueable:在 startTest/stopTest 之间调用 System.enqueueJob()——测试中的链式调用深度限制为 1
  • Schedulable:在 startTest/stopTest 之间调用 System.schedule()——之后断言 CronTrigger
Platform Event 和 CDC 测试
  • Platform Events:发布后调用 Test.getEventBus().deliver(),强制同步传递
  • Change Data Capture:在测试设置中调用 Test.enableChangeDataCapture(),然后在 DML 后调用 Test.getEventBus().deliver()
Stub API(依赖注入)

使用 System.StubProvider 接口和 Test.createStub() 模拟依赖项,无需访问数据库。

Test.loadData()

从 Static Resource 中的 CSV 加载批量测试数据:Test.loadData(Account.sObjectType, 'TestAccounts')

Mixed DML 变通方法

使用 System.runAs(),在同一测试中将设置对象的 DML(User、Profile)与非设置对象的 DML 分开。

特殊对象测试
  • 对 Product2/PricebookEntry 测试使用 Test.getStandardPricebookId()
  • 对 @RestResource 端点测试使用 RestContext.request = new RestRequest()

注意事项

  • @TestSetup 数据会在各测试方法间共享(不会隔离)——每个方法都会获得一份副本,且副本会重置
  • SeeAllData=true 会暴露生产数据——几乎不要使用
  • Future/Batch/Queueable 会在 Test.stopTest() 之后执行,而不是在其执行期间
  • 必须在 Test.startTest() 之前注册 Callout 模拟(Test.setMock())
  • 测试中不保证 Platform Event 的顺序
  • 每个测试方法只能调用一次 Test.startTest() / Test.stopTest()
  • Batch Apex 的 finish() 方法也会在 Test.stopTest() 之后运行
  • Mixed DML 会抛出 MIXED_DML_OPERATION——使用 System.runAs() 解决

工作流

  1. 使用 Read/Glob 工具读取待测类
  2. 确认所有 public/global 方法和代码路径
  3. 检查是否存在 TestDataFactory;如果不存在则创建
  4. 生成包含所有场景类型的测试类
  5. 运行测试:sf apex run test -n MyClassTest --synchronous --code-coverage
  6. 报告覆盖率并修复所有失败

参考资料

  • 测试模式 — 异步测试、Platform Events、CDC、Stub API、REST 端点、Mixed DML、Flow 测试覆盖率
  • 治理限制 — 测试上下文中每个事务的限制

评论

支持 Markdown