Everyone makes testing mistakes β but the same mistakes get made over and over by developers at every experience level. This guide collects the most damaging and most common JUnit testing anti-patterns, explains exactly why each is harmful, and shows the correct approach with before/after code. Every example below uses current JUnit 6 (Jupiter) API β org.junit.jupiter.api.Assertions static imports, current annotations β and calls out the spots where JUnit 6 specifically changes the calculus versus JUnit 5. Work through this list once and you will never make these mistakes again.
Mistake 1: Testing Implementation, Not Behaviour
The most fundamental mistake. Tests that verify how code works internally break every time the implementation changes, even if the behaviour stays correct.
// WRONG: tests internal implementation details
@Test
void testOrderService() {
orderService.placeOrder(order);
// Verifying internal calls β breaks if you refactor without changing behaviour
verify(orderRepository).findByCustomerId(anyLong());
verify(cacheService).invalidate("orders:customer:1");
verify(metricsService).increment("orders.placed");
verify(auditLogger).log(any(AuditEvent.class));
// This test breaks if you rename a method, add a cache layer, or change logging
}
// CORRECT: tests observable behaviour
@Test
@DisplayName("Placing an order persists it and confirms via email")
void placingOrderPersistsItAndSendsConfirmation() {
Order placed = orderService.placeOrder(order);
// Verify what the consumer of this service observes
assertNotNull(placed.getId(), "Order must be assigned an ID");
assertEquals(OrderStatus.CONFIRMED, placed.getStatus());
verify(emailService).sendConfirmation(order.getCustomerEmail()); // external contract
// Internal caching, metrics, auditing: tested in their own unit tests
}
Mistake 2: Assertions Without Messages
// WRONG: failure message is cryptic
assertEquals("CONFIRMED", order.getStatus().name());
// Failure: expected: but was:
// No context: WHICH order? After WHAT operation? In WHAT test scenario?
// CORRECT: failure message explains context
assertEquals(OrderStatus.CONFIRMED, order.getStatus(),
"Order should be CONFIRMED after successful payment authorisation, "
+ "got: " + order.getStatus()
+ ", payment result: " + order.getPaymentResult());
// Failure message now contains everything needed to diagnose without running the test again
Continue reading Common JUnit Testing Mistakes and How to Avoid Them →