@salesforce/afv-skills 1.5.1 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +16 -416
  2. package/package.json +5 -3
  3. package/skills/building-ui-bundle-app/SKILL.md +325 -0
  4. package/skills/building-ui-bundle-frontend/SKILL.md +122 -0
  5. package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/component.md +1 -1
  6. package/skills/creating-b2b-commerce-store/SKILL.md +169 -0
  7. package/skills/creating-b2b-commerce-store/references/store-vs-storefront.md +169 -0
  8. package/skills/deploying-ui-bundle/SKILL.md +77 -0
  9. package/skills/generating-apex/CREDITS.md +30 -0
  10. package/skills/generating-apex/SKILL.md +342 -189
  11. package/skills/generating-apex/assets/abstract.cls +12 -9
  12. package/skills/generating-apex/assets/batch.cls +7 -8
  13. package/skills/generating-apex/assets/domain.cls +5 -6
  14. package/skills/generating-apex/assets/dto.cls +11 -12
  15. package/skills/generating-apex/assets/exception.cls +1 -2
  16. package/skills/generating-apex/assets/interface.cls +2 -3
  17. package/skills/generating-apex/assets/invocable.cls +114 -0
  18. package/skills/generating-apex/assets/queueable.cls +6 -7
  19. package/skills/generating-apex/assets/rest-resource.cls +300 -0
  20. package/skills/generating-apex/assets/schedulable.cls +7 -8
  21. package/skills/generating-apex/assets/selector.cls +7 -8
  22. package/skills/generating-apex/assets/service.cls +4 -5
  23. package/skills/generating-apex/assets/trigger.cls +45 -0
  24. package/skills/generating-apex/assets/utility.cls +5 -6
  25. package/skills/generating-apex/references/AccountDeduplicationBatch.cls +7 -8
  26. package/skills/generating-apex/references/AccountSelector.cls +10 -11
  27. package/skills/generating-apex/references/AccountService.cls +9 -10
  28. package/skills/generating-apex-test/CREDITS.md +30 -0
  29. package/skills/generating-apex-test/SKILL.md +165 -74
  30. package/skills/generating-apex-test/assets/test-class-template.cls +25 -56
  31. package/skills/generating-apex-test/assets/test-data-factory-template.cls +0 -1
  32. package/skills/generating-apex-test/references/assertion-patterns.md +38 -95
  33. package/skills/generating-apex-test/references/async-testing.md +59 -142
  34. package/skills/generating-apex-test/references/mocking-patterns.md +77 -76
  35. package/skills/generating-apex-test/references/test-data-factory.md +29 -130
  36. package/skills/generating-experience-react-site/SKILL.md +9 -9
  37. package/skills/generating-experience-react-site/docs/configure-metadata-digital-experience.md +1 -1
  38. package/skills/generating-flexipage/SKILL.md +28 -12
  39. package/skills/generating-ui-bundle-features/SKILL.md +45 -0
  40. package/skills/generating-ui-bundle-metadata/SKILL.md +106 -0
  41. package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/SKILL.md +5 -5
  42. package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/constraints.md +2 -2
  43. package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/examples.md +1 -1
  44. package/skills/{implementing-webapp-file-upload → implementing-ui-bundle-file-upload}/SKILL.md +11 -11
  45. package/skills/searching-media/SKILL.md +1 -1
  46. package/skills/uplifting-components-to-slds2/SKILL.md +236 -0
  47. package/skills/uplifting-components-to-slds2/references/color-hooks-decision-guide.md +438 -0
  48. package/skills/uplifting-components-to-slds2/references/common-patterns.md +87 -0
  49. package/skills/uplifting-components-to-slds2/references/examples.md +443 -0
  50. package/skills/uplifting-components-to-slds2/references/migration-checklist.md +67 -0
  51. package/skills/uplifting-components-to-slds2/references/non-color-hooks-decision-guide.md +333 -0
  52. package/skills/uplifting-components-to-slds2/references/rule-lwc-token-to-slds-hook.md +135 -0
  53. package/skills/uplifting-components-to-slds2/references/rule-no-deprecated-tokens-slds1.md +211 -0
  54. package/skills/uplifting-components-to-slds2/references/rule-no-hardcoded-values.md +160 -0
  55. package/skills/uplifting-components-to-slds2/references/rule-no-slds-class-overrides.md +126 -0
  56. package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data}/SKILL.md +52 -25
  57. package/skills/using-ui-bundle-salesforce-data/references/mutation-query-generation.md +140 -0
  58. package/skills/using-ui-bundle-salesforce-data/references/query-testing.md +78 -0
  59. package/skills/using-ui-bundle-salesforce-data/references/read-query-generation.md +307 -0
  60. package/skills/using-ui-bundle-salesforce-data/references/schema-introspection.md +53 -0
  61. package/skills/using-ui-bundle-salesforce-data/references/ui-bundle-integration.md +221 -0
  62. package/skills/{using-webapp-salesforce-data → using-ui-bundle-salesforce-data/scripts}/graphql-search.sh +75 -23
  63. package/skills/building-webapp-data-visualization/SKILL.md +0 -72
  64. package/skills/building-webapp-data-visualization/implementation/bar-line-chart.md +0 -316
  65. package/skills/building-webapp-data-visualization/implementation/dashboard-layout.md +0 -189
  66. package/skills/building-webapp-data-visualization/implementation/donut-chart.md +0 -181
  67. package/skills/building-webapp-data-visualization/implementation/stat-card.md +0 -150
  68. package/skills/building-webapp-react-components/SKILL.md +0 -96
  69. package/skills/configuring-webapp-csp-trusted-sites/SKILL.md +0 -90
  70. package/skills/configuring-webapp-metadata/SKILL.md +0 -158
  71. package/skills/creating-webapp/SKILL.md +0 -138
  72. package/skills/deploying-webapp-to-salesforce/SKILL.md +0 -226
  73. package/skills/installing-webapp-features/SKILL.md +0 -210
  74. /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/header-footer.md +0 -0
  75. /package/skills/{building-webapp-react-components → building-ui-bundle-frontend}/implementation/page.md +0 -0
  76. /package/skills/{configuring-webapp-csp-trusted-sites/implementation/metadata-format.md → generating-ui-bundle-metadata/implementation/csp-metadata-format.md} +0 -0
  77. /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/style-tokens.md +0 -0
  78. /package/skills/{managing-webapp-agentforce-conversation-client → implementing-ui-bundle-agentforce-conversation-client}/references/troubleshooting.md +0 -0
@@ -1,15 +1,14 @@
1
1
  /**
2
- * @description Selector class for Account queries.
2
+ * Selector class for Account queries.
3
3
  * Encapsulates all SOQL for Account records.
4
4
  * All methods return bulkified results (Lists or Maps).
5
- * @author Generated by Apex Class Writer Skill
6
5
  */
7
6
  public with sharing class AccountSelector {
8
7
 
9
8
  // ─── Field Lists ─────────────────────────────────────────────────────
10
9
 
11
10
  /**
12
- * @description Returns the default set of fields to query for Account.
11
+ * Returns the default set of fields to query for Account.
13
12
  * Centralizes field references to keep queries DRY.
14
13
  * @return Comma-separated field list as a String
15
14
  */
@@ -34,7 +33,7 @@ public with sharing class AccountSelector {
34
33
  }
35
34
 
36
35
  /**
37
- * @description Returns fields needed for billing/territory operations
36
+ * Returns fields needed for billing/territory operations
38
37
  * @return Comma-separated field list as a String
39
38
  */
40
39
  private static String getBillingFields() {
@@ -56,7 +55,7 @@ public with sharing class AccountSelector {
56
55
  // ─── Query Methods ───────────────────────────────────────────────────
57
56
 
58
57
  /**
59
- * @description Selects Account records by their Ids
58
+ * Selects Account records by their Ids
60
59
  * @param recordIds Set of Account Ids to query
61
60
  * @return List of Account records matching the provided Ids
62
61
  * @example
@@ -76,7 +75,7 @@ public with sharing class AccountSelector {
76
75
  }
77
76
 
78
77
  /**
79
- * @description Selects Account records as a Map keyed by Id
78
+ * Selects Account records as a Map keyed by Id
80
79
  * @param recordIds Set of Account Ids to query
81
80
  * @return Map of Id to Account
82
81
  */
@@ -85,7 +84,7 @@ public with sharing class AccountSelector {
85
84
  }
86
85
 
87
86
  /**
88
- * @description Selects Accounts with billing address fields for territory assignment
87
+ * Selects Accounts with billing address fields for territory assignment
89
88
  * @param recordIds Set of Account Ids to query
90
89
  * @return List of Account records with billing address fields populated
91
90
  */
@@ -102,7 +101,7 @@ public with sharing class AccountSelector {
102
101
  }
103
102
 
104
103
  /**
105
- * @description Selects Accounts by Account Type
104
+ * Selects Accounts by Account Type
106
105
  * @param accountTypes Set of Account Type values to filter by
107
106
  * @return List of matching Account records
108
107
  * @example
@@ -124,7 +123,7 @@ public with sharing class AccountSelector {
124
123
  }
125
124
 
126
125
  /**
127
- * @description Selects Accounts by Industry with a minimum annual revenue
126
+ * Selects Accounts by Industry with a minimum annual revenue
128
127
  * @param industries Set of Industry values to filter by
129
128
  * @param minRevenue Minimum AnnualRevenue threshold
130
129
  * @return List of matching Account records ordered by revenue descending
@@ -151,7 +150,7 @@ public with sharing class AccountSelector {
151
150
  }
152
151
 
153
152
  /**
154
- * @description Selects Accounts with their related Contacts (subquery)
153
+ * Selects Accounts with their related Contacts (subquery)
155
154
  * @param recordIds Set of Account Ids to query
156
155
  * @return List of Account records with nested Contacts
157
156
  */
@@ -173,7 +172,7 @@ public with sharing class AccountSelector {
173
172
  // ─── Aggregate Queries ───────────────────────────────────────────────
174
173
 
175
174
  /**
176
- * @description Returns a count of Accounts grouped by Industry
175
+ * Returns a count of Accounts grouped by Industry
177
176
  * @return List of AggregateResult with Industry and record count
178
177
  * @example
179
178
  * List<AggregateResult> results = AccountSelector.countByIndustry();
@@ -1,8 +1,7 @@
1
1
  /**
2
- * @description Service class for Account business logic.
2
+ * Service class for Account business logic.
3
3
  * Provides account deduplication, enrichment, and territory assignment.
4
4
  * Delegates queries to AccountSelector and SObject manipulation to AccountDomain.
5
- * @author Generated by Apex Class Writer Skill
6
5
  */
7
6
  public with sharing class AccountService {
8
7
 
@@ -14,7 +13,7 @@ public with sharing class AccountService {
14
13
  // ─── Public API ──────────────────────────────────────────────────────
15
14
 
16
15
  /**
17
- * @description Merges duplicate Account records into a master record.
16
+ * Merges duplicate Account records into a master record.
18
17
  * The master record retains its field values; child records are reparented.
19
18
  * @param masterIds Map of master Account Id to Set of duplicate Account Ids to merge
20
19
  * @return List of master Account Ids that were successfully merged
@@ -77,7 +76,7 @@ public with sharing class AccountService {
77
76
  }
78
77
 
79
78
  /**
80
- * @description Assigns accounts to territories based on Billing State/Country.
79
+ * Assigns accounts to territories based on Billing State/Country.
81
80
  * Uses Custom Metadata Type (Territory_Mapping__mdt) for mappings.
82
81
  * @param accountIds Set of Account Ids to assign territories for
83
82
  * @return Number of accounts successfully updated
@@ -115,7 +114,7 @@ public with sharing class AccountService {
115
114
  // ─── Convenience Overloads ───────────────────────────────────────────
116
115
 
117
116
  /**
118
- * @description Single-account territory assignment convenience method
117
+ * Single-account territory assignment convenience method
119
118
  * @param accountId The Account Id to assign a territory for
120
119
  * @return 1 if updated, 0 if no change needed
121
120
  */
@@ -126,7 +125,7 @@ public with sharing class AccountService {
126
125
  // ─── Private Helpers ─────────────────────────────────────────────────
127
126
 
128
127
  /**
129
- * @description Loads territory mappings from Custom Metadata
128
+ * Loads territory mappings from Custom Metadata
130
129
  * @return Map of territory key (State:Country) to territory name
131
130
  */
132
131
  private static Map<String, String> loadTerritoryMappings() {
@@ -139,7 +138,7 @@ public with sharing class AccountService {
139
138
  }
140
139
 
141
140
  /**
142
- * @description Builds a consistent territory lookup key
141
+ * Builds a consistent territory lookup key
143
142
  * @param state The billing state
144
143
  * @param country The billing country
145
144
  * @return A normalized key string
@@ -149,7 +148,7 @@ public with sharing class AccountService {
149
148
  }
150
149
 
151
150
  /**
152
- * @description Chunks a list of Accounts into sublists of the given size
151
+ * Chunks a list of Accounts into sublists of the given size
153
152
  * @param accounts The accounts to chunk
154
153
  * @param chunkSize Maximum chunk size
155
154
  * @return List of account sublists
@@ -172,7 +171,7 @@ public with sharing class AccountService {
172
171
  }
173
172
 
174
173
  /**
175
- * @description Counts successful results from a DML operation
174
+ * Counts successful results from a DML operation
176
175
  * @param results List of Database.SaveResult
177
176
  * @return Count of successful operations
178
177
  */
@@ -195,7 +194,7 @@ public with sharing class AccountService {
195
194
  // ─── Exception ───────────────────────────────────────────────────────
196
195
 
197
196
  /**
198
- * @description Custom exception for AccountService errors
197
+ * Custom exception for AccountService errors
199
198
  */
200
199
  public class AccountServiceException extends Exception {}
201
200
  }
@@ -0,0 +1,30 @@
1
+ # Credits & Acknowledgments
2
+
3
+ This skill was influenced by the [sf-skills](https://github.com/Jaganpro/sf-skills) repository and built upon the collective wisdom of the Salesforce developer community. We gratefully acknowledge the following authors and resources whose ideas, patterns, and best practices have shaped this skill.
4
+
5
+ ---
6
+
7
+ ## Authors & Contributors
8
+
9
+ ### Jag Valaiyapathy (**[Jaganpro)](https://github.com/Jaganpro)**
10
+
11
+ **[sf-skills](https://github.com/Jaganpro/sf-skills)**
12
+
13
+ Key contributions influencing this skill:
14
+
15
+ - Pioneering open-source Salesforce skills for agentic coding tools
16
+ - Apex code generation and review patterns
17
+ - Best practices, anti-patterns, and design patterns reference material
18
+ - Template library for common Apex class types
19
+
20
+ This skill was influenced by the [sf-skills](https://github.com/Jaganpro/sf-skills) repository.
21
+
22
+ ---
23
+
24
+ ## Special Thanks
25
+
26
+ To the entire Salesforce developer community for sharing knowledge, writing blogs, creating open-source tools, and helping each other build better solutions.
27
+
28
+ ---
29
+
30
+ *If we've missed anyone whose work influenced these skills, please let us know so we can add proper attribution.*
@@ -1,108 +1,199 @@
1
1
  ---
2
2
  name: generating-apex-test
3
- description: Apex test class generation with TestDataFactory patterns, bulk testing (200+ records), mocking strategies, and assertion best practices. Use this skill when the user asks to create, write, or improve Apex test classes, add coverage, build mocks, or implement testing patterns for triggers, services, batch jobs, queueables, and integrations.
3
+ description: Generate and validate Apex test classes with TestDataFactory patterns, bulk testing (251+ records), mocking strategies, assertion best practices, and disciplined test-fix loops. Use this skill when creating new Apex test classes, improving test coverage, debugging and fixing failing Apex tests, running test execution and coverage analysis, or implementing testing patterns for triggers, services, controllers, batch jobs, queueables, and integrations. Triggers on *Test.cls, *_Test.cls files, sf apex run test workflows, coverage reports, test-fix loops. Do NOT trigger for production Apex code (use generating-apex) or Jest/LWC tests.
4
4
  ---
5
5
 
6
- # Apex Test Class Skill
6
+ # Generating Apex Tests
7
+
8
+ Generate production-ready Apex test classes and run disciplined test-fix loops with coverage analysis.
7
9
 
8
10
  ## Core Principles
9
11
 
10
- 1. **Bulkify tests** - Always test with 200+ records to catch governor limit issues
11
- 2. **Isolate test data** - Use `@TestSetup` and TestDataFactory; never rely on org data
12
- 3. **Assert meaningfully** - Test behavior, not just coverage; include failure messages
13
- 4. **Mock external dependencies** - Use `HttpCalloutMock`, `Test.setMock()` for integrations
14
- 5. **Test negative paths** - Validate error handling, not just happy paths
12
+ 1. **One behavior per method** each test method validates a single scenario. Separate positive, negative, and bulk tests. NEVER combine related-but-distinct inputs (e.g., null and empty) in one method — create `_NullInput_` and `_EmptyInput_` as separate test methods
13
+ 2. **Bulkify tests** — test with 251+ records to cross the 200-record trigger batch boundary. **Batch Apex exception:** in test context only one `execute()` invocation runs, so set `batchSize >= testRecordCount`. See [references/async-testing.md](references/async-testing.md)
14
+ 3. **Isolate test data** every `@TestSetup` must delegate record creation to a `TestDataFactory` class. If none exists, create one first. Never build record lists inline in `@TestSetup`. Never rely on org data (`SeeAllData=false`) or hardcoded IDs. For duplicate rule handling, see [references/test-data-factory.md](references/test-data-factory.md)
15
+ 4. **Assert meaningfully** use exact expected values computed from test data setup. NEVER use range assertions or approximate counts when the value is deterministic. Always include failure messages. See [references/assertion-patterns.md](references/assertion-patterns.md)
16
+ 5. **Use `Assert` class only** `Assert.areEqual`, `Assert.isTrue`, `Assert.fail`, etc. Never use legacy `System.assert`, `System.assertEquals`, or `System.assertNotEquals`
17
+ 6. **Mock external boundaries** — use `HttpCalloutMock` for callouts, `Test.setFixedSearchResults` for SOSL, DML mock classes for database isolation. Design for testability via constructor injection. See [references/mocking-patterns.md](references/mocking-patterns.md)
18
+ 7. **Test negative paths** — validate error handling and exception scenarios, not just happy paths
19
+ 8. **Wrap with start/stop** — pair `Test.startTest()` with `Test.stopTest()` to reset governor limits and force async execution
20
+
21
+ ## Test.startTest() / Test.stopTest()
22
+
23
+ Always wrap the code under test in `Test.startTest()` / `Test.stopTest()`:
24
+
25
+ - Resets governor limits so the test measures only the code under test
26
+ - Executes async operations synchronously (queueables, batch, future methods)
27
+ - Fires scheduled jobs immediately
28
+
29
+ ## Test Code Anti-Patterns
30
+
31
+ | Anti-Pattern | Fix |
32
+ |---|---|
33
+ | SOQL/DML inside loops | Query once before the loop; use `Map<Id, SObject>` for lookups |
34
+ | Magic numbers in assertions | Derive expected values from setup constants |
35
+ | God test class (>500 lines) | Split into multiple test classes by behavior area |
36
+ | Long test methods (>30 lines) | Extract Given/When/Then into helper methods |
37
+ | Generic `Exception` catch | Catch the specific expected type (e.g., `DmlException`) |
38
+
39
+ ## Workflow
40
+
41
+ ### Step 1 — Gather Context
42
+
43
+ Before generating or fixing tests, identify:
44
+
45
+ - the target production class(es) under test
46
+ - existing test classes, test data factories, and setup helpers
47
+ - desired test scope (single class, specific methods, suite, or local tests)
48
+ - coverage threshold (75% minimum for deploy, 90%+ recommended)
49
+ - org alias when running tests against an org
15
50
 
16
- ## Test Class Structure
51
+ ### Step 2 — Generate the Test Class
52
+
53
+ Apply the structure, naming conventions, and patterns from the asset templates and reference docs.
54
+
55
+ **MANDATORY — File Deliverables:** For every test class, create BOTH files:
56
+ 1. `{ClassName}Test.cls` — the test class (use [assets/test-class-template.cls](assets/test-class-template.cls) as starting point)
57
+ 2. `{ClassName}Test.cls-meta.xml` — the metadata file:
58
+
59
+ ```xml
60
+ <?xml version="1.0" encoding="UTF-8"?>
61
+ <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
62
+ <apiVersion>66.0</apiVersion>
63
+ <status>Active</status>
64
+ </ApexClass>
65
+ ```
66
+
67
+ If no `TestDataFactory` exists in the project, create `TestDataFactory.cls` + `TestDataFactory.cls-meta.xml` using [assets/test-data-factory-template.cls](assets/test-data-factory-template.cls).
68
+
69
+ #### @TestSetup Example
70
+
71
+ ```apex
72
+ @TestSetup
73
+ static void setupTestData() {
74
+ List<Account> accounts = TestDataFactory.createAccounts(251, true);
75
+ }
76
+ ```
77
+
78
+ #### Test Method Structure
79
+
80
+ Use Given/When/Then:
17
81
 
18
82
  ```apex
19
83
  @isTest
20
- private class MyServiceTest {
84
+ static void shouldUpdateStatus_WhenValidInput() {
85
+ // Given
86
+ List<Account> accounts = [SELECT Id FROM Account];
87
+
88
+ // When
89
+ Test.startTest();
90
+ MyService.processAccounts(accounts);
91
+ Test.stopTest();
92
+
93
+ // Then
94
+ List<Account> updated = [SELECT Id, Status__c FROM Account];
95
+ Assert.areEqual(251, updated.size(), 'All accounts should be processed');
96
+ }
97
+ ```
21
98
 
22
- @TestSetup
23
- static void setupTestData() {
24
- // Create shared test data using TestDataFactory
25
- List<Account> accounts = TestDataFactory.createAccounts(200, true);
26
- }
99
+ #### Negative Test — Exception Pattern
27
100
 
28
- @isTest
29
- static void shouldPerformExpectedBehavior_WhenValidInput() {
30
- // Given: Setup specific test state
31
- List<Account> accounts = [SELECT Id, Name FROM Account];
32
-
33
- // When: Execute the code under test
34
- Test.startTest();
35
- MyService.processAccounts(accounts);
36
- Test.stopTest();
37
-
38
- // Then: Assert expected outcomes
39
- List<Account> updated = [SELECT Id, Status__c FROM Account];
40
- System.assertEquals(200, updated.size(), 'All accounts should be processed');
41
- for (Account acc : updated) {
42
- System.assertEquals('Processed', acc.Status__c, 'Status should be updated');
43
- }
44
- }
101
+ Use try/catch with `Assert.fail` to verify expected exceptions:
45
102
 
46
- @isTest
47
- static void shouldThrowException_WhenInvalidInput() {
48
- // Given
49
- List<Account> emptyList = new List<Account>();
50
-
51
- // When/Then
52
- Test.startTest();
53
- try {
54
- MyService.processAccounts(emptyList);
55
- System.assert(false, 'Expected MyCustomException to be thrown');
56
- } catch (MyCustomException e) {
57
- System.assert(e.getMessage().contains('cannot be empty'),
58
- 'Exception message should indicate empty input');
59
- }
60
- Test.stopTest();
103
+ ```apex
104
+ @isTest
105
+ static void shouldThrowException_WhenInvalidInput() {
106
+ // Given
107
+ List<Account> emptyList = new List<Account>();
108
+
109
+ // When/Then
110
+ Test.startTest();
111
+ try {
112
+ MyService.processAccounts(emptyList);
113
+ Assert.fail('Expected MyCustomException to be thrown');
114
+ } catch (MyCustomException e) {
115
+ Assert.isTrue(e.getMessage().contains('cannot be empty'),
116
+ 'Exception message should indicate empty input');
61
117
  }
118
+ Test.stopTest();
62
119
  }
63
120
  ```
64
121
 
65
- ## Naming Convention
122
+ #### Naming Convention
66
123
 
67
- Use descriptive method names: `should[ExpectedBehavior]_When[Condition]`
124
+ - `should[ExpectedResult]_When[Scenario]`: `shouldSendNotification_WhenOpportunityClosedWon`
125
+ - `[SubjectOrAction]_[Scenario]_[ExpectedResult]`: `AccountUpdate_ChangeName_Success`
68
126
 
69
- Examples:
70
- - `shouldCreateContact_WhenAccountIsActive`
71
- - `shouldThrowException_WhenEmailIsInvalid`
72
- - `shouldSendNotification_WhenOpportunityClosedWon`
73
- - `shouldBypassTrigger_WhenRunningAsBatch`
127
+ ### Step 3 — Run Tests
74
128
 
75
- ## Test.startTest() / Test.stopTest()
129
+ Start narrow when debugging; widen after the fix is stable.
76
130
 
77
- Always wrap the code under test:
78
- - Resets governor limits for accurate limit testing
79
- - Executes async operations synchronously (queueables, batch, future)
80
- - Fires scheduled jobs immediately
131
+ ```bash
132
+ # Single test class
133
+ sf apex run test --class-names MyServiceTest --result-format human --code-coverage --target-org <alias>
81
134
 
82
- ## Asset Templates
135
+ # Specific test methods
136
+ sf apex run test --tests MyServiceTest.shouldUpdateStatus_WhenValidInput --result-format human --target-org <alias>
83
137
 
84
- Ready-to-use scaffolds for common test patterns:
138
+ # All local tests
139
+ sf apex run test --test-level RunLocalTests --result-format human --code-coverage --target-org <alias>
140
+ ```
85
141
 
86
- - **[assets/test-class-template.cls](assets/test-class-template.cls)** - Starter test class with positive, negative, bulk, and governor limit test stubs
87
- - **[assets/test-data-factory-template.cls](assets/test-data-factory-template.cls)** - TestDataFactory with Account, Contact, Opportunity, User factories and field override support
142
+ ### Step 4 Analyze Results
88
143
 
89
- ## Reference Files
144
+ Focus on:
90
145
 
91
- Detailed patterns for specific scenarios:
146
+ - failing methods exception types and stack traces
147
+ - uncovered lines and weak coverage areas
148
+ - whether failures indicate bad test data, brittle assertions, or broken production logic
92
149
 
93
- - **[references/test-data-factory.md](references/test-data-factory.md)** - TestDataFactory class patterns and field defaults
94
- - **[references/assertion-patterns.md](references/assertion-patterns.md)** - Assertion best practices and common pitfalls
95
- - **[references/mocking-patterns.md](references/mocking-patterns.md)** - HttpCalloutMock, Test.setMock(), stubbing
96
- - **[references/async-testing.md](references/async-testing.md)** - Batch, Queueable, Future, Scheduled job testing
150
+ ### Step 5 Fix Loop
97
151
 
98
- ## Quick Reference: What to Test
152
+ When tests fail, run a disciplined fix loop (max 3 iterations — stop and surface root cause if still failing):
153
+
154
+ 1. Read the failing test class and the class under test
155
+ 2. Identify root cause from error messages and stack traces
156
+ 3. Apply fix — adjust test data or assertions for test-side issues; delegate production code issues to the `generating-apex` skill
157
+ 4. Rerun the focused test before broader regression
158
+ 5. Repeat until all tests pass, iteration limit reached, or root cause requires design change
159
+
160
+ ### Step 6 — Validate Coverage
161
+
162
+ | Level | Coverage | Purpose |
163
+ |-------|----------|---------|
164
+ | Production deploy | 75% minimum | Required by Salesforce |
165
+ | Recommended | 90%+ | Best practice target |
166
+ | Critical paths | 100% | Business-critical code |
167
+
168
+ Cover all paths: positive, negative/exception, bulk (251+ records), callout/async.
169
+
170
+ ## What to Test by Component
99
171
 
100
172
  | Component | Key Test Scenarios |
101
173
  |-----------|-------------------|
102
- | Trigger | Bulk insert/update/delete, recursion, field changes |
103
- | Service | Valid/invalid inputs, bulk operations, exceptions |
174
+ | Trigger | Bulk insert/update/delete, recursion guard, field change detection |
175
+ | Service | Valid/invalid inputs, bulk operations, exception handling |
104
176
  | Controller | Page load, action methods, view state |
105
- | Batch | Start/execute/finish, chunking, error records |
106
- | Queueable | Chaining, bulkification, error handling |
177
+ | Batch | start/execute/finish, scope matching (batch size >= record count), `Database.Stateful` tracking, error handling, chaining (separate methods — `finish()` calling `Database.executeBatch()` throws `UnexpectedException`) |
178
+ | Queueable | Chaining (only first job runs in tests), bulkification, error handling, callout mocks before `Test.startTest()` |
107
179
  | Callout | Success response, error response, timeout |
108
- | Scheduled | Execution, CRON validation |
180
+ | Selector | Valid/null/empty inputs, bulk (251+), field population, sort order, `WITH USER_MODE` via `System.runAs` |
181
+ | Scheduled | Direct execution via `execute(null)`, CRON registration via `CronTrigger` query |
182
+ | Platform Event | `Test.enableChangeDataCapture()`, `Test.getEventBus().deliver()`, verify subscriber side effects |
183
+
184
+ ## Output Expectations
185
+
186
+ Deliverables per test class:
187
+ - `{ClassName}Test.cls` + `{ClassName}Test.cls-meta.xml` (match API version of class under test; default `66.0`)
188
+ - `TestDataFactory.cls` + `TestDataFactory.cls-meta.xml` (if not already present)
189
+
190
+ ## Reference Files
191
+
192
+ Load on demand for detailed patterns:
193
+
194
+ | Reference | When to use |
195
+ |-----------|-------------|
196
+ | [references/test-data-factory.md](references/test-data-factory.md) | TestDataFactory patterns, field overrides, duplicate rule handling |
197
+ | [references/assertion-patterns.md](references/assertion-patterns.md) | Assertion best practices, anti-patterns, common pitfalls |
198
+ | [references/mocking-patterns.md](references/mocking-patterns.md) | HttpCalloutMock, DML mocking, StubProvider, SOSL, Email, Platform Events |
199
+ | [references/async-testing.md](references/async-testing.md) | Batch, Queueable, Future, Scheduled job testing |
@@ -1,51 +1,47 @@
1
1
  /**
2
- * @description Test class for {ClassUnderTest}.
3
- * Tests bulk operations (200+ records), positive/negative paths,
4
- * and exception handling.
5
- * @author Generated by Apex Test Writer Skill
2
+ * Test class for {ClassUnderTest}.
3
+ * Tests bulk operations (251+ records), positive/negative paths,
4
+ * and exception handling.
6
5
  */
7
6
  @isTest
8
7
  private class {ClassUnderTest}Test {
9
8
 
10
- // ─── Test Setup ───────────────────────────────────────────────────────
11
-
12
9
  @TestSetup
13
10
  static void setupTestData() {
14
- // Create shared test data using TestDataFactory
15
- // List<Account> accounts = TestDataFactory.createAccounts(200, true);
11
+ List<Account> accounts = TestDataFactory.createAccounts(251, true);
16
12
  }
17
13
 
18
14
  // ─── Positive Tests ───────────────────────────────────────────────────
19
15
 
20
16
  @isTest
21
17
  static void shouldPerformExpectedBehavior_WhenValidInput() {
22
- // Given: Setup specific test state
23
- // List<Account> accounts = [SELECT Id, Name FROM Account];
18
+ // Given
19
+ List<Account> accounts = [SELECT Id, Name FROM Account];
24
20
 
25
- // When: Execute the code under test
21
+ // When
26
22
  Test.startTest();
27
23
  // {ClassUnderTest}.methodUnderTest(params);
28
24
  Test.stopTest();
29
25
 
30
- // Then: Assert expected outcomes
31
- // System.assertEquals(expected, actual, 'Descriptive failure message');
26
+ // Then
27
+ // Assert.areEqual(expected, actual, 'Descriptive failure message');
32
28
  }
33
29
 
34
30
  @isTest
35
- static void shouldHandleBulkRecords_WhenProcessing200() {
36
- // Given: 200+ records to verify bulkification
37
- // List<Account> accounts = [SELECT Id FROM Account];
38
- // System.assertEquals(200, accounts.size(), 'Should have 200 test records');
31
+ static void shouldHandleBulkRecords_WhenProcessing251() {
32
+ // Given
33
+ List<Account> accounts = [SELECT Id FROM Account];
34
+ Assert.areEqual(251, accounts.size(), 'Should have 251 test records');
39
35
 
40
36
  // When
41
37
  Test.startTest();
42
38
  // {ClassUnderTest}.bulkMethod(accounts);
43
39
  Test.stopTest();
44
40
 
45
- // Then: Verify all records processed
41
+ // Then
46
42
  // List<Account> results = [SELECT Id, Status__c FROM Account];
47
43
  // for (Account acc : results) {
48
- // System.assertEquals('Processed', acc.Status__c, 'All records should be processed');
44
+ // Assert.areEqual('Processed', acc.Status__c, 'All records should be processed');
49
45
  // }
50
46
  }
51
47
 
@@ -53,21 +49,15 @@ private class {ClassUnderTest}Test {
53
49
 
54
50
  @isTest
55
51
  static void shouldThrowException_WhenNullInput() {
56
- Boolean exceptionThrown = false;
57
- String exceptionMessage = '';
58
-
59
52
  Test.startTest();
60
53
  try {
61
54
  // {ClassUnderTest}.methodUnderTest(null);
62
- } catch (Exception e) {
63
- exceptionThrown = true;
64
- exceptionMessage = e.getMessage();
55
+ Assert.fail('Expected exception for null input');
56
+ } catch (MyCustomException e) {
57
+ Assert.isTrue(e.getMessage().contains('cannot be null'),
58
+ 'Exception message should mention null input');
65
59
  }
66
60
  Test.stopTest();
67
-
68
- System.assert(exceptionThrown, 'Exception should be thrown for null input');
69
- System.assert(exceptionMessage.contains('cannot be null'),
70
- 'Exception message should mention null input');
71
61
  }
72
62
 
73
63
  @isTest
@@ -76,16 +66,16 @@ private class {ClassUnderTest}Test {
76
66
  // List<SObject> results = {ClassUnderTest}.methodUnderTest(new List<Id>());
77
67
  Test.stopTest();
78
68
 
79
- // System.assert(results.isEmpty(), 'Should return empty list for empty input');
69
+ // Assert.isTrue(results.isEmpty(), 'Should return empty list for empty input');
80
70
  }
81
71
 
82
72
  // ─── Edge Case Tests ──────────────────────────────────────────────────
83
73
 
84
74
  @isTest
85
75
  static void shouldHandleMixedRecords_WhenSomeQualify() {
86
- // Given: Mix of qualifying and non-qualifying records
87
- // List<Account> accounts = [SELECT Id, Status__c FROM Account];
88
- // Integer half = accounts.size() / 2;
76
+ // Given
77
+ List<Account> accounts = [SELECT Id, Status__c FROM Account];
78
+ Integer half = accounts.size() / 2;
89
79
  // for (Integer i = 0; i < half; i++) {
90
80
  // accounts[i].Status__c = 'Qualifying';
91
81
  // }
@@ -96,29 +86,8 @@ private class {ClassUnderTest}Test {
96
86
  // {ClassUnderTest}.conditionalMethod(accounts);
97
87
  Test.stopTest();
98
88
 
99
- // Then: Only qualifying records should be affected
89
+ // Then
100
90
  // List<Account> qualifying = [SELECT Id FROM Account WHERE Processed__c = true];
101
- // System.assertEquals(half, qualifying.size(), 'Only qualifying records should be processed');
102
- }
103
-
104
- // ─── Governor Limit Tests ─────────────────────────────────────────────
105
-
106
- @isTest
107
- static void shouldNotExceedGovernorLimits_WhenBulkProcessing() {
108
- // Given
109
- // List<Account> accounts = [SELECT Id FROM Account];
110
-
111
- Test.startTest();
112
- // {ClassUnderTest}.heavyMethod(accounts);
113
- Test.stopTest();
114
-
115
- System.assert(Limits.getDmlStatements() < Limits.getLimitDmlStatements(),
116
- 'Should not exceed DML statement limit');
117
- System.assert(Limits.getQueries() < Limits.getLimitQueries(),
118
- 'Should not exceed SOQL query limit');
91
+ // Assert.areEqual(half, qualifying.size(), 'Only qualifying records should be processed');
119
92
  }
120
-
121
- // ─── Helper Methods ───────────────────────────────────────────────────
122
-
123
- // Add test-specific helper methods here
124
93
  }
@@ -2,7 +2,6 @@
2
2
  * @description Centralized factory for creating test data with sensible defaults.
3
3
  * All methods accept a doInsert flag for flexibility.
4
4
  * Bulk methods create multiple records; single-record methods delegate to bulk.
5
- * @author Generated by Apex Test Writer Skill
6
5
  */
7
6
  @isTest
8
7
  public class TestDataFactory {