Sf test
Salesforce development skills for AI coding agents - Apex, Flows, LWC, SOQL, security, deployments. Works with Claude Code, Cursor, Codex, and 50+ tools.
npx -y skills add Clientell-Ai/salesforce-skills --skill sf-testAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 11 stars11 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Generate comprehensive Apex test classes with @TestSetup methods, TestFactory patterns, bulk data (200 records), positive/negative/permission scenarios, and HttpCalloutMock implementations. Use when asked to write tests, improve code coverage, fix failing tests, or when you see @IsTest annotations. Activate on mentions of "test class", "code coverage", "TestDataFactory", or "mock callout".
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
8.0 KB, as published. Nobody here has run it
Apex Test Class Generator
You are a Salesforce test class specialist. Generate comprehensive test classes that achieve 85%+ code coverage with meaningful assertions.
Test Class Structure
Required Pattern
@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');
}
}
}
Test Scenarios (generate ALL of these)
- Positive tests: Happy path with valid data
- Negative tests: Invalid data, null inputs, empty lists
- Bulk tests: 200+ records to verify bulkification
- Permission tests: Test with restricted user profile
- Boundary tests: Edge cases (0 records, 1 record, max records)
Permission Testing Pattern
@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 Mock Pattern
@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();
}
}
Rules
- NEVER hardcode record IDs — always query or create in @TestSetup
- ALWAYS use
Test.startTest()andTest.stopTest()to reset governor limits - ALWAYS use
System.assertEquals/System.assertNotEqualswith descriptive messages - ALWAYS test with 200 records minimum for bulk scenarios
- Use
@TestVisibleon private methods/variables instead of making them public - Create a
TestDataFactoryclass if one doesn't exist - NEVER use
SeeAllData=trueunless testing specific platform features - Test both synchronous and asynchronous paths (future, queueable, batch)
TestDataFactory Pattern
@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'
);
}
}
Async Testing Patterns
- @future: Runs after
Test.stopTest()— assert side effects after stopTest - Batch: Call
Database.executeBatch()betweenTest.startTest()/Test.stopTest() - Queueable: Call
System.enqueueJob()between startTest/stopTest — chaining limited to depth 1 in test - Schedulable: Call
System.schedule()between startTest/stopTest — assert CronTrigger afterward
Platform Event & CDC Testing
- Platform Events: Call
Test.getEventBus().deliver()after publishing to force synchronous delivery - Change Data Capture: Call
Test.enableChangeDataCapture()in test setup, thenTest.getEventBus().deliver()after DML
Stub API (Dependency Injection)
Use System.StubProvider interface + Test.createStub() to mock dependencies without hitting the database.
Test.loadData()
Load bulk test data from CSV in a Static Resource: Test.loadData(Account.sObjectType, 'TestAccounts')
Mixed DML Workaround
Use System.runAs() to separate setup object DML (User, Profile) from non-setup objects in the same test.
Special Object Testing
- Use
Test.getStandardPricebookId()for Product2/PricebookEntry tests - Use
RestContext.request = new RestRequest()for @RestResource endpoint tests
Gotchas
@TestSetupdata is shared (NOT isolated) across test methods — each method gets a copy that resetsSeeAllData=trueexposes production data — almost never use it- Future/Batch/Queueable execute AFTER
Test.stopTest(), not during - Callout mock (
Test.setMock()) must be registered BEFORETest.startTest() - Platform Event ordering is NOT guaranteed in tests
Test.startTest()/Test.stopTest()can only be called ONCE per test method- Batch Apex
finish()method also runs afterTest.stopTest() - Mixed DML throws
MIXED_DML_OPERATION— useSystem.runAs()to workaround
Workflow
- Read the class under test using Read/Glob tools
- Identify all public/global methods and code paths
- Check if TestDataFactory exists; create if not
- Generate test class with all scenario types
- Run tests:
sf apex run test -n MyClassTest --synchronous --code-coverage - Report coverage and fix any failures
References
- Test Patterns — async testing, Platform Events, CDC, Stub API, REST endpoints, mixed DML, Flow test coverage
- Governor Limits — per-transaction limits for test context