mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-20 08:05:12 +00:00
- 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.
4.8 KiB
4.8 KiB
@DataJpaTest
Testing JPA repositories with isolated data layer slice.
Basic Structure
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
@Autowired
private OrderRepository orderRepository;
@Autowired
private TestEntityManager entityManager;
}
What Gets Loaded
- Repository beans
- EntityManager / TestEntityManager
- DataSource
- Transaction manager
- No web layer, no services, no controllers
Testing Custom Queries
@Test
void shouldFindOrdersByStatus() {
// Given - Using var for cleaner code
var pending = new Order("PENDING");
var completed = new Order("COMPLETED");
entityManager.persist(pending);
entityManager.persist(completed);
entityManager.flush();
// When
var pendingOrders = orderRepository.findByStatus("PENDING");
// Then - Using sequenced collection methods
assertThat(pendingOrders).hasSize(1);
assertThat(pendingOrders.getFirst().getStatus()).isEqualTo("PENDING");
}
Testing Native Queries
@Test
void shouldExecuteNativeQuery() {
entityManager.persist(new Order("PENDING", BigDecimal.valueOf(100)));
entityManager.persist(new Order("PENDING", BigDecimal.valueOf(200)));
entityManager.flush();
var total = orderRepository.calculatePendingTotal();
assertThat(total).isEqualTo(new BigDecimal("300.00"));
}
Testing Pagination
@Test
void shouldReturnPagedResults() {
// Insert 20 orders using IntStream
IntStream.range(0, 20).forEach(i -> {
entityManager.persist(new Order("PENDING"));
});
entityManager.flush();
var page = orderRepository.findByStatus("PENDING", PageRequest.of(0, 10));
assertThat(page.getContent()).hasSize(10);
assertThat(page.getTotalElements()).isEqualTo(20);
assertThat(page.getContent().getFirst().getStatus()).isEqualTo("PENDING");
}
Testing Lazy Loading
@Test
void shouldLazyLoadOrderItems() {
var order = new Order("PENDING");
order.addItem(new OrderItem("Product", 2));
entityManager.persist(order);
entityManager.flush();
entityManager.clear(); // Detach from persistence context
var found = orderRepository.findById(order.getId());
assertThat(found).isPresent();
// This will trigger lazy loading
assertThat(found.get().getItems()).hasSize(1);
assertThat(found.get().getItems().getFirst().getProduct()).isEqualTo("Product");
}
Testing Cascading
@Test
void shouldCascadeDelete() {
var order = new Order("PENDING");
order.addItem(new OrderItem("Product", 2));
entityManager.persist(order);
entityManager.flush();
orderRepository.delete(order);
entityManager.flush();
assertThat(entityManager.find(OrderItem.class, order.getItems().getFirst().getId()))
.isNull();
}
Testing @Query Methods
@Query("SELECT o FROM Order o WHERE o.createdAt > :date AND o.status = :status")
List<Order> findRecentByStatus(@Param("date") LocalDateTime date,
@Param("status") String status);
@Test
void shouldFindRecentOrders() {
var old = new Order("PENDING");
old.setCreatedAt(LocalDateTime.now().minusDays(10));
var recent = new Order("PENDING");
recent.setCreatedAt(LocalDateTime.now().minusHours(1));
entityManager.persist(old);
entityManager.persist(recent);
entityManager.flush();
var recentOrders = orderRepository.findRecentByStatus(
LocalDateTime.now().minusDays(1), "PENDING");
assertThat(recentOrders).hasSize(1);
assertThat(recentOrders.getFirst().getId()).isEqualTo(recent.getId());
}
Using H2 vs Real Database
H2 (Default - Not Recommended for Production Parity)
@DataJpaTest // Uses embedded H2 by default
class OrderRepositoryH2Test {
// Fast but may miss DB-specific issues
}
Testcontainers (Recommended)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
class OrderRepositoryPostgresTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:18");
}
Transaction Behavior
Tests are @Transactional by default and roll back after each test.
@Test
@Rollback(false) // Don't roll back (rarely needed)
void shouldPersistData() {
orderRepository.save(new Order("PENDING"));
// Data will remain in database after test
}
Key Points
- Use TestEntityManager for setup data
- Always flush() after persist() to trigger SQL
- Clear() the entity manager to test lazy loading
- Use real database (Testcontainers) for accurate results
- Test both success and failure cases
- Leverage Java 25 var keyword for cleaner variable declarations
- Use sequenced collection methods (getFirst(), getLast(), reversed())