mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-20 16:15:12 +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:
207
skills/spring-boot-testing/references/assertj-basics.md
Normal file
207
skills/spring-boot-testing/references/assertj-basics.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# AssertJ Basics
|
||||
|
||||
Fluent assertions for readable, maintainable tests.
|
||||
|
||||
## Basic Assertions
|
||||
|
||||
### Object Equality
|
||||
|
||||
```java
|
||||
assertThat(order.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(order.getId()).isNotEqualTo(0);
|
||||
assertThat(order).isEqualTo(expectedOrder);
|
||||
assertThat(order).isNotNull();
|
||||
assertThat(nullOrder).isNull();
|
||||
```
|
||||
|
||||
### String Assertions
|
||||
|
||||
```java
|
||||
assertThat(order.getDescription())
|
||||
.isEqualTo("Test Order")
|
||||
.startsWith("Test")
|
||||
.endsWith("Order")
|
||||
.contains("Test")
|
||||
.hasSize(10)
|
||||
.matches("[A-Za-z ]+");
|
||||
```
|
||||
|
||||
### Number Assertions
|
||||
|
||||
```java
|
||||
assertThat(order.getAmount())
|
||||
.isEqualTo(99.99)
|
||||
.isGreaterThan(50)
|
||||
.isLessThan(100)
|
||||
.isBetween(50, 100)
|
||||
.isPositive()
|
||||
.isNotZero();
|
||||
```
|
||||
|
||||
### Boolean Assertions
|
||||
|
||||
```java
|
||||
assertThat(order.isActive()).isTrue();
|
||||
assertThat(order.isDeleted()).isFalse();
|
||||
```
|
||||
|
||||
## Date/Time Assertions
|
||||
|
||||
```java
|
||||
assertThat(order.getCreatedAt())
|
||||
.isEqualTo(LocalDateTime.of(2024, 1, 15, 10, 30))
|
||||
.isBefore(LocalDateTime.now())
|
||||
.isAfter(LocalDateTime.of(2024, 1, 1))
|
||||
.isCloseTo(LocalDateTime.now(), within(5, ChronoUnit.SECONDS));
|
||||
```
|
||||
|
||||
## Optional Assertions
|
||||
|
||||
```java
|
||||
Optional<Order> maybeOrder = orderService.findById(1L);
|
||||
|
||||
assertThat(maybeOrder)
|
||||
.isPresent()
|
||||
.hasValueSatisfying(order -> {
|
||||
assertThat(order.getId()).isEqualTo(1L);
|
||||
});
|
||||
|
||||
assertThat(orderService.findById(999L)).isEmpty();
|
||||
```
|
||||
|
||||
## Exception Assertions
|
||||
|
||||
### JUnit 5 Exception Handling
|
||||
|
||||
```java
|
||||
@Test
|
||||
void shouldThrowException() {
|
||||
OrderService service = new OrderService();
|
||||
|
||||
assertThatThrownBy(() -> service.findById(999L))
|
||||
.isInstanceOf(OrderNotFoundException.class)
|
||||
.hasMessage("Order 999 not found")
|
||||
.hasMessageContaining("999");
|
||||
}
|
||||
```
|
||||
|
||||
### AssertJ Exception Handling
|
||||
|
||||
```java
|
||||
@Test
|
||||
void shouldThrowExceptionWithCause() {
|
||||
assertThatExceptionOfType(OrderProcessingException.class)
|
||||
.isThrownBy(() -> service.processOrder(invalidOrder))
|
||||
.withCauseInstanceOf(ValidationException.class);
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Assertions
|
||||
|
||||
Create domain-specific assertions for reusable test code:
|
||||
|
||||
```java
|
||||
public class OrderAssert extends AbstractAssert<OrderAssert, Order> {
|
||||
|
||||
public static OrderAssert assertThat(Order actual) {
|
||||
return new OrderAssert(actual);
|
||||
}
|
||||
|
||||
private OrderAssert(Order actual) {
|
||||
super(actual, OrderAssert.class);
|
||||
}
|
||||
|
||||
public OrderAssert isPending() {
|
||||
isNotNull();
|
||||
if (!"PENDING".equals(actual.getStatus())) {
|
||||
failWithMessage("Expected order status to be PENDING but was %s", actual.getStatus());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public OrderAssert hasTotal(BigDecimal expected) {
|
||||
isNotNull();
|
||||
if (!expected.equals(actual.getTotal())) {
|
||||
failWithMessage("Expected total %s but was %s", expected, actual.getTotal());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```java
|
||||
OrderAssert.assertThat(order)
|
||||
.isPending()
|
||||
.hasTotal(new BigDecimal("99.99"));
|
||||
```
|
||||
|
||||
## Soft Assertions
|
||||
|
||||
Collect multiple failures before failing:
|
||||
|
||||
```java
|
||||
@Test
|
||||
void shouldValidateOrder() {
|
||||
Order order = orderService.findById(1L);
|
||||
|
||||
SoftAssertions.assertSoftly(softly -> {
|
||||
softly.assertThat(order.getId()).isEqualTo(1L);
|
||||
softly.assertThat(order.getStatus()).isEqualTo("PENDING");
|
||||
softly.assertThat(order.getItems()).isNotEmpty();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Satisfies Pattern
|
||||
|
||||
```java
|
||||
assertThat(order)
|
||||
.satisfies(o -> {
|
||||
assertThat(o.getId()).isPositive();
|
||||
assertThat(o.getStatus()).isNotBlank();
|
||||
assertThat(o.getCreatedAt()).isNotNull();
|
||||
});
|
||||
```
|
||||
|
||||
## Using with Spring
|
||||
|
||||
```java
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringBootTest
|
||||
class OrderServiceTest {
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Test
|
||||
void shouldCreateOrder() {
|
||||
Order order = orderService.create(new OrderRequest("Product", 2));
|
||||
|
||||
assertThat(order)
|
||||
.isNotNull()
|
||||
.extracting(Order::getId, Order::getStatus)
|
||||
.containsExactly(1L, "PENDING");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Static Import
|
||||
|
||||
Always use static import for clean assertions:
|
||||
|
||||
```java
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
1. **Readable**: Sentence-like structure
|
||||
2. **Type-safe**: IDE autocomplete works
|
||||
3. **Rich API**: Many built-in assertions
|
||||
4. **Extensible**: Custom assertions for your domain
|
||||
5. **Better Errors**: Clear failure messages
|
||||
Reference in New Issue
Block a user