mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-21 00:25:13 +00:00
feat: add spring-boot-testing skill for Spring Boot 4.0 (#1085)
- Introduced MockMvcTester for AssertJ-style assertions in Spring MVC testing. - Added @RestClientTest for testing REST clients with MockRestServiceServer. - Implemented RestTestClient as a modern alternative to TestRestTemplate. - Documented migration steps from Spring Boot 3.x to 4.0, including dependency and annotation changes. - Created an overview of test slices to guide testing strategies. - Included Testcontainers setup for JDBC testing with PostgreSQL and MySQL. - Enhanced @WebMvcTest documentation with examples for various HTTP methods and validation.
This commit is contained in:
230
skills/spring-boot-testing/references/instancio.md
Normal file
230
skills/spring-boot-testing/references/instancio.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# Instancio
|
||||
|
||||
Generate complex test objects automatically. Use when entities/DTOs have 3+ properties.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Objects with **3 or more properties**
|
||||
- Setting up test data for repositories
|
||||
- Creating DTOs for controller tests
|
||||
- Avoiding repetitive builder/setter calls
|
||||
|
||||
## Dependency
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.instancio</groupId>
|
||||
<artifactId>instancio-junit</artifactId>
|
||||
<version>5.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Simple Object
|
||||
|
||||
```java
|
||||
final var order = Instancio.create(Order.class);
|
||||
// All fields populated with random data
|
||||
```
|
||||
|
||||
### List of Objects
|
||||
|
||||
```java
|
||||
final var orders = Instancio.ofList(Order.class).size(5).create();
|
||||
// 5 orders with random data
|
||||
```
|
||||
|
||||
## Customizing Values
|
||||
|
||||
### Set Specific Fields
|
||||
|
||||
```java
|
||||
final var order = Instancio.of(Order.class)
|
||||
.set(field(Order::getStatus), "PENDING")
|
||||
.set(field(Order::getTotal), new BigDecimal("99.99"))
|
||||
.create();
|
||||
```
|
||||
|
||||
### Supply Generated Values
|
||||
|
||||
```java
|
||||
final var order = Instancio.of(Order.class)
|
||||
.supply(field(Order::getEmail), () -> "user" + UUID.randomUUID() + "@test.com")
|
||||
.create();
|
||||
```
|
||||
|
||||
### Ignore Fields
|
||||
|
||||
```java
|
||||
final var order = Instancio.of(Order.class)
|
||||
.ignore(field(Order::getId)) // Let DB generate
|
||||
.create();
|
||||
```
|
||||
|
||||
## Complex Objects
|
||||
|
||||
### Nested Objects
|
||||
|
||||
```java
|
||||
final var order = Instancio.of(Order.class)
|
||||
.set(field(Order::getCustomer), Instancio.create(Customer.class))
|
||||
.set(field(Order::getItems), Instancio.ofList(OrderItem.class).size(3).create())
|
||||
.create();
|
||||
```
|
||||
|
||||
### All Fields Random
|
||||
|
||||
```java
|
||||
// When you need fully random but valid data
|
||||
final var randomOrder = Instancio.create(Order.class);
|
||||
// Customer, items, addresses - all populated
|
||||
```
|
||||
|
||||
## Spring Boot Integration
|
||||
|
||||
### Repository Test Setup
|
||||
|
||||
```java
|
||||
@DataJpaTest
|
||||
@AutoConfigureTestDatabase
|
||||
@Testcontainers
|
||||
class OrderRepositoryTest {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
|
||||
|
||||
@Autowired
|
||||
private OrderRepository orderRepository;
|
||||
|
||||
@Test
|
||||
void shouldFindOrdersByStatus() {
|
||||
// Given: Create 10 random orders with PENDING status
|
||||
final var orders = Instancio.ofList(Order.class)
|
||||
.size(10)
|
||||
.set(field(Order::getStatus), "PENDING")
|
||||
.create();
|
||||
|
||||
orderRepository.saveAll(orders);
|
||||
|
||||
// When
|
||||
final var found = orderRepository.findByStatus("PENDING");
|
||||
|
||||
// Then
|
||||
assertThat(found).hasSize(10);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Controller Test Setup
|
||||
|
||||
```java
|
||||
@WebMvcTest(OrderController.class)
|
||||
class OrderControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvcTester mvc;
|
||||
|
||||
@MockitoBean
|
||||
private OrderService orderService;
|
||||
|
||||
@Test
|
||||
void shouldReturnOrder() {
|
||||
// Given: Random order with specific ID
|
||||
Order order = Instancio.of(Order.class)
|
||||
.set(field(Order::getId), 1L)
|
||||
.create();
|
||||
|
||||
given(orderService.findById(1L)).willReturn(order);
|
||||
|
||||
// When/Then
|
||||
assertThat(mvc.get().uri("/orders/1"))
|
||||
.hasStatus(HttpStatus.OK)
|
||||
.bodyJson()
|
||||
.convertTo(OrderResponse.class)
|
||||
.satisfies(response -> {
|
||||
assertThat(response.getId()).isEqualTo(1L);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Patterns
|
||||
|
||||
### Builder Pattern Alternative
|
||||
|
||||
```java
|
||||
// Instead of:
|
||||
Order order = Order.builder()
|
||||
.id(1L)
|
||||
.status("PENDING")
|
||||
.customer(Customer.builder().name("John").build())
|
||||
.items(List.of(
|
||||
OrderItem.builder().product("A").price(10).build(),
|
||||
OrderItem.builder().product("B").price(20).build()
|
||||
))
|
||||
.build();
|
||||
|
||||
// Use:
|
||||
Order order = Instancio.of(Order.class)
|
||||
.set(field(Order::getId), 1L)
|
||||
.set(field(Order::getStatus), "PENDING")
|
||||
.create();
|
||||
// Customer and items auto-generated
|
||||
```
|
||||
|
||||
### Seeded Data
|
||||
|
||||
```java
|
||||
// Consistent "random" data for reproducible tests
|
||||
Order order = Instancio.of(Order.class)
|
||||
.withSeed(12345L)
|
||||
.create();
|
||||
// Same data every test run with seed 12345
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Email Generation
|
||||
|
||||
```java
|
||||
String email = Instancio.gen().net().email();
|
||||
```
|
||||
|
||||
### Date Generation
|
||||
|
||||
```java
|
||||
LocalDateTime createdAt = Instancio.gen().temporal()
|
||||
.localDateTime()
|
||||
.past()
|
||||
.create();
|
||||
```
|
||||
|
||||
### String Patterns
|
||||
|
||||
```java
|
||||
String phone = Instancio.gen().text().pattern("+1-###-###-####");
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
| Approach | Lines of Code | Maintainability |
|
||||
| -------- | ------------- | --------------- |
|
||||
| Manual setters | 10-20 | Low |
|
||||
| Builder pattern | 5-10 | Medium |
|
||||
| **Instancio** | 2-5 | **High** |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use for 3+ property objects** - Not worth it for simple objects
|
||||
2. **Set only what's relevant** - Let Instancio fill the rest
|
||||
3. **Use with Testcontainers** - Great for database seeding
|
||||
4. **Set IDs explicitly** - When testing specific scenarios
|
||||
5. **Ignore auto-generated fields** - Like createdAt, updatedAt
|
||||
|
||||
## Links
|
||||
|
||||
- [Instancio Documentation](https://www.instancio.org/)
|
||||
- [JUnit 5 Extension](https://www.instancio.org/user-guide/#junit-integration)
|
||||
Reference in New Issue
Block a user