@@ -93,12 +93,21 @@ void testName() {
9393
9494## Test Development Guidelines
9595
96- ### 1. Remove Overlapping Test Cases
96+ ### 1. Test Structure
97+ - Implement ` RewriteTest ` interface
98+ - Override ` defaults(RecipeSpec spec) ` to set the recipe being tested
99+ - Use ` @DocumentExample ` on the primary test case that best demonstrates the recipe
100+ - Add ` @Issue("https://github.com/...") ` annotations for tests related to specific issues
101+
102+ ### 2. Test Naming
103+ - Use descriptive test method names that explain the scenario being tested
104+ - Prefix negative tests with ` doNotChange ` or ` unchanged `
105+ - Group related test scenarios (e.g., ` removeRedundantNullCheck ` , ` removeRedundantNullCheckWithMethodInvocation ` )
106+
107+ ### 3. Test Case Design
97108- Avoid test cases that essentially test the same functionality
98109- Each test should cover a distinct scenario or edge case
99110- Consolidate similar tests when they don't add unique value
100-
101- ### 2. Use Method Parameters Instead of Local Variables
102111- In test text blocks, prefer method parameters over local variable declarations
103112- This makes tests more concise and focuses on the transformation being tested
104113- Example:
@@ -119,11 +128,70 @@ void testName() {
119128 }
120129 ```
121130
122- ### 3. Suppress Warnings Appropriately
123- - Add ` @SuppressWarnings ` annotations to test classes to suppress expected warnings
124- - Common suppressions for this type of recipe:
131+ ### 4. Suppress Warnings Appropriately
132+ - Add ` @SuppressWarnings ` at the class level for expected warnings in test code:
125133 - ` "ConstantConditions" ` - for redundant null checks that the recipe will remove
126- - Other suppressions as needed based on the specific warnings in test cases
134+ - ` "ConditionCoveredByFurtherCondition" ` - for redundant conditions
135+ - ` "RedundantCast" ` - for unnecessary casts
136+ - ` "unused" ` - for unused variables/methods
137+
138+ ### 5. Language Comments for IDE Support
139+ - Add ` //language=java ` before ` java() ` calls when there's no customization
140+ - Place comments on individual strings when there's customization or multiple ` java() ` calls
141+ - Don't add language comments to JavaTemplate strings with parameters
142+
143+ ### 6. Test Coverage Guidelines
144+ - Test basic functionality with simple cases
145+ - Include edge cases (method invocations, field access, complex expressions)
146+ - Test cases where the recipe should NOT make changes
147+ - Test partial transformations in complex conditions
148+ - Test different operator positions and combinations
149+ - Include tests for language-specific behavior (e.g., Kotlin's ` !is ` operator)
150+
151+ ## Recipe Implementation Conventions
152+
153+ ### 1. Recipe Structure
154+ - Use ` @EqualsAndHashCode(callSuper = false) ` and ` @Value ` from Lombok for immutable recipe classes
155+ - Implement ` getEstimatedEffortPerOccurrence() ` - use the same time estimate from SonarQube when implementing RSPEC rules
156+ - Always include RSPEC tags in ` getTags() ` when implementing SonarQube rules (e.g., ` "RSPEC-S1697" ` )
157+ - Use ` Preconditions.check() ` to exclude specific file types (e.g., Kotlin files) when a recipe is language-specific
158+
159+ ### 2. Visitor Choice
160+ - Use ` JavaIsoVisitor<ExecutionContext> ` when returning the same type of LST element being visited
161+ - Use ` JavaVisitor<ExecutionContext> ` when the transformation changes the tree structure or returns a different type
162+ - For multi-language support, implement separate visitors for each language (JavaVisitor, KotlinVisitor, etc.)
163+
164+ ### 3. Expression Comparison
165+ - Always use ` SemanticallyEqual.areEqual() ` for comparing expressions instead of string matching
166+ - Use ` J.Literal.isLiteralValue() ` for checking literal values
167+ - Use ` TypeUtils ` helper methods (e.g., ` TypeUtils.asArray() ` , ` TypeUtils.asFullyQualified() ` ) instead of instanceof checks with JavaType
168+
169+ ### 4. Method Matching
170+ - Use ` MethodMatcher ` for reliable method matching instead of manual name/type checks
171+ - Example: ` private static final MethodMatcher TO_ARRAY = new MethodMatcher("java.util.Collection toArray()", true); `
172+
173+ ### 5. Refaster Templates
174+ - Simple transformations can use Refaster-style templates with ` @BeforeTemplate ` and ` @AfterTemplate ` annotations
175+ - Use ` @RecipeDescriptor ` annotation for metadata on Refaster template classes
176+
177+ ### 6. JavaTemplate Best Practices
178+ - Always declare imports when templates introduce types: ` .imports(fqn) `
179+ - Call ` maybeAddImport() ` after applying templates that use new types
180+ - Use ` service(ImportService.class).shortenFullyQualifiedTypeReferencesIn() ` to clean up imports
181+ - Don't add ` //language=java ` comments to JavaTemplate strings containing parameters like ` #{any()} `
182+
183+ ## YAML Configuration
184+
185+ ### Recipe Collections
186+ Recipes are organized into YAML files in ` src/main/resources/META-INF/rewrite/ ` :
187+ - ` common-static-analysis.yml ` - Common static analysis fixes that apply across languages
188+ - ` java-best-practices.yml ` - Java-specific best practices and idioms
189+ - ` static-analysis.yml ` - General static analysis recipes
190+
191+ ### Adding Recipes to Collections
192+ - Add new recipes to appropriate YAML files based on their scope
193+ - Comment out recipes that are experimental or have known issues (with a comment explaining why)
194+ - Group related recipes together in the YAML lists
127195
128196## Important Notes
129197
0 commit comments