-
Notifications
You must be signed in to change notification settings - Fork 116
Add Cursor Rule for Fixture Monkey Test Writing Guidelines #1195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Thank you for your contribution! There are two things I would like to see improved. FirstThe following conditions are used to select the introspector for use in the SecondReplace the String path expression with exp (javaGetter, kotlin DSL Exp) https://naver.github.io/fixture-monkey/v1-1-0/docs/plugins/kotlin-plugin/kotlin-exp/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for feedback!
I've added both improvements you requested.
All changes are based on the official documentation you referenced.
| ## FixtureMonkey Instance Creation | ||
|
|
||
| ### Basic Setup | ||
| ```java | ||
| // Basic setup with default options | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.create(); | ||
|
|
||
| // Kotlin setup with Kotlin plugin | ||
| val fixtureMonkey = FixtureMonkey | ||
| .plugin(KotlinPlugin()) | ||
| .build(); | ||
| ``` | ||
|
|
||
| ### Advanced Configuration with Introspector Selection | ||
|
|
||
| Choose the appropriate introspector based on your class characteristics: | ||
|
|
||
| ```java | ||
| // For classes with constructor parameters matching field names | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) | ||
| .defaultNotNull(true) | ||
| .build(); | ||
|
|
||
| // For classes with public fields | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE) | ||
| .defaultNotNull(true) | ||
| .build(); | ||
|
|
||
| // For classes with setter methods | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(BeanArbitraryIntrospector.INSTANCE) | ||
| .defaultNotNull(true) | ||
| .build(); | ||
|
|
||
| // For classes implementing Builder pattern | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(BuilderArbitraryIntrospector.INSTANCE) | ||
| .defaultNotNull(true) | ||
| .build(); | ||
|
|
||
| // For fail-safe approach (tries multiple introspectors) | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(FailoverIntrospector.from( | ||
| ConstructorPropertiesArbitraryIntrospector.INSTANCE, | ||
| FieldReflectionArbitraryIntrospector.INSTANCE, | ||
| BeanArbitraryIntrospector.INSTANCE | ||
| )) | ||
| .defaultNotNull(true) | ||
| .build(); | ||
| ``` | ||
|
|
||
| #### Introspector Selection Guide: | ||
| - **ConstructorPropertiesArbitraryIntrospector**: Use when constructor parameters match field names (records, data classes) | ||
| - **FieldReflectionArbitraryIntrospector**: Use when class has public fields | ||
| - **BeanArbitraryIntrospector**: Use when class follows JavaBean conventions (getter/setter methods) | ||
| - **BuilderArbitraryIntrospector**: Use when class implements Builder pattern | ||
| - **FailoverIntrospector**: Use when you're unsure or need to support multiple class types |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added Introspector Selection Guidelines
- Added comprehensive guide for choosing the right introspector based on class characteristics
- Included
ConstructorPropertiesArbitraryIntrospector,FieldReflectionArbitraryIntrospector,BeanArbitraryIntrospector,BuilderArbitraryIntrospector, andFailoverIntrospector
| ### 1. Type-Safe Object Customization | ||
|
|
||
| **Java - Use javaGetter() for type-safe field selection:** | ||
| ```java | ||
| // Setting specific values with type safety | ||
| fixtureMonkey.giveMeBuilder(Product.class) | ||
| .set(javaGetter(Product::getName), "Test Product") | ||
| .set(javaGetter(Product::getPrice), 100.0) | ||
| .sample(); | ||
|
|
||
| // Setting collection sizes - Always set size before customizing elements | ||
| fixtureMonkey.giveMeBuilder(Order.class) | ||
| .size(javaGetter(Order::getItems), 3) // Set size first | ||
| .set(javaGetter(Order::getItems).index(0, Item::getName), "Item 1") // Then customize elements | ||
| .sample(); | ||
|
|
||
| // Nested object customization | ||
| fixtureMonkey.giveMeBuilder(Order.class) | ||
| .set(javaGetter(Order::getCustomer, Customer::getName), "John Doe") | ||
| .set(javaGetter(Order::getItems).index(0, Item::getPrice), 100.0) | ||
| .sample(); | ||
|
|
||
| // Using Arbitrary values | ||
| fixtureMonkey.giveMeBuilder(Product.class) | ||
| .set(javaGetter(Product::getPrice), Arbitraries.longs().greaterThan(0)) | ||
| .sample(); | ||
| ``` | ||
|
|
||
| **Kotlin - Use Kotlin DSL Exp for type-safe field selection:** | ||
| ```kotlin | ||
| // Setting specific values with type safety | ||
| fixtureMonkey.giveMeBuilder<Product>() | ||
| .setExp(Product::name, "Test Product") | ||
| .setExp(Product::price, 100.0) | ||
| .sample() | ||
|
|
||
| // Setting collection sizes - Always set size before customizing elements | ||
| fixtureMonkey.giveMeBuilder<Order>() | ||
| .sizeExp(Order::items, 3) // Set size first | ||
| .setExp(Order::items["0"] into Item::name, "Item 1") // Then customize elements using Kotlin DSL | ||
| .sample() | ||
|
|
||
| // Nested object customization | ||
| fixtureMonkey.giveMeBuilder<Order>() | ||
| .setExp(Order::customer into Customer::name, "John Doe") | ||
| .setExp(Order::items["0"] into Item::price, 100.0) | ||
| .sample() | ||
|
|
||
| // Using Arbitrary values | ||
| fixtureMonkey.giveMeBuilder<Product>() | ||
| .setExp(Product::price, Arbitraries.longs().greaterThan(0)) | ||
| .sample() | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced String Path Expressions with Type-Safe Alternatives
- Java: Replaced all string expressions with
javaGetter()method references - Kotlin: Used proper Kotlin DSL with
setExp(),setExpGetter(),into,intoGetter
| 7. **Using wrong introspector for class type** | ||
| ```java | ||
| // Don't do this - using wrong introspector | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(BeanArbitraryIntrospector.INSTANCE) // Wrong for record classes | ||
| .build(); | ||
|
|
||
| // For record classes, use ConstructorPropertiesArbitraryIntrospector | ||
| public record User(String name, int age) {} | ||
|
|
||
| // Do this instead | ||
| FixtureMonkey fixtureMonkey = FixtureMonkey.builder() | ||
| .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) // Correct for records | ||
| .build(); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhanced Anti-Patterns Section
- Shows how using wrong introspector for class type can cause issues - e.g., using
BeanArbitraryIntrospectorfor record classes instead of the correctConstructorPropertiesArbitraryIntrospector
| 1. What type of class are you working with? | ||
| - Record/Data class → Use `ConstructorPropertiesArbitraryIntrospector` | ||
| - Public fields → Use `FieldReflectionArbitraryIntrospector` | ||
| - JavaBean (getter/setter) → Use `BeanArbitraryIntrospector` | ||
| - Builder pattern → Use `BuilderArbitraryIntrospector` | ||
| - Mixed or unsure → Use `FailoverIntrospector` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhanced Decision Tree Section
- Reorganized decision tree to prioritize introspector selection as the first step, ensuring users choose the right introspector before proceeding with other customizations
| 8. Are you working with collections? | ||
| - Yes → Always set size before customizing elements using type-safe expressions | ||
| - No → Proceed with customization using type-safe expressions | ||
|
|
||
| 9. Are you using Java or Kotlin? | ||
| - Java → Use `javaGetter()` for type-safe field selection | ||
| - Kotlin → Use Kotlin DSL with `setExp()`, `setExpGetter()`, `into`, `intoGetter` syntax |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhanced Decision Tree Section
- Added collection handling step emphasizing size-first approach with type-safe expressions
- Added language-specific guidance for choosing between Java
javaGetter()and Kotlin DSL (setExp(),setExpGetter(),into,intoGetter)
seongahjo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well done! It looks great!
Thank you for contributing!
Summary
This PR implements a Cursor IDE rule file for FixtureMonkey test writing guidelines to help developers write more
effective and consistent tests using FixtureMonkey patterns and best practices. (#1190)
(Optional): Description
Added
cursor/rules/fixture-monkey-test-writing-rule.mdcthat provides intelligent code suggestions for FixtureMonkeyusage, including:
giveMeOne()vsgiveMeBuilder()vsgiveMe()).set(),.size(), and builder methodsHow Has This Been Tested?
1. Spring Boot REST API Project
Scenario: Simple POJO object creation
Test Cases: User service testing with basic field customization
Results: ✅ Proper giveMeBuilder() suggestions, correct .set() usage guidance
Before (without the rule):
After (with Cursor rule guidance):
2. JPA Entities Complex Domain Model
Scenario: Complex entity relationships with @manytoone
Test Cases: Order/Customer/OrderItem entity creation
Results: ✅ Appropriate handling of entity relationships, .size() method suggestions for collections
Before (without the rule):
After (with Cursor rule guidance):
3. Microservice DTO Structures
Scenario: Complex DTOs with nested objects, Maps, and generic types
Test Cases: ProductResponse with CategoryDto, PriceInfo, Map<String, Object>
Results: ✅ Proper generic type handling, nested object customization guidance
Before (without the rule):
After (with Cursor rule guidance):
4. Legacy Codebase with Manual Test Setup
Scenario: Existing complex manual object creation code
Test Cases: Employee objects with Address, ContactInfo, and complex Maps
Results: ✅ Successfully detected manual patterns and suggested FixtureMonkey alternatives
Before (without the rule):
After (with Cursor rule guidance):
5. Kotlin Data Class Projects
Scenario: Kotlin-specific features (nullable types, default values)
Test Cases: Product data classes with nullable fields and collections
Results: ✅ Proper handling of Kotlin nullability and default value features
Before (without the rule):
After (with Cursor rule guidance):
Is the Document updated?
We recommend that the corresponding documentation for this feature or change is updated within the pull request
If the update is scheduled for later, please specify and add the necessary information to
the discussion page.