← Back to Module 3
Testing Best Practices
Test Organization
Project Structure
src/
├── main/
│ └── java/
│ └── com/example/
│ ├── controller/
│ ├── service/
│ └── repository/
└── test/
└── java/
└── com/example/
├── controller/
├── service/
└── repository/
Test Class Organization
public class UserServiceTest {
// 1. Test fixtures and setup
@BeforeEach
void setup() {
// Setup code
}
// 2. Happy path tests
@Test
void createUser_ValidData_ReturnsUser() {
// Test code
}
// 3. Edge cases
@Test
void createUser_EmptyName_ThrowsException() {
// Test code
}
// 4. Error cases
@Test
void createUser_DuplicateEmail_ThrowsException() {
// Test code
}
}
Test Naming Conventions
Method Naming
// MethodName_Scenario_ExpectedResult
@Test
void calculateTotal_WithValidInputs_ReturnsCorrectSum() {
// Test code
}
@Test
void processOrder_WithInvalidQuantity_ThrowsIllegalArgumentException() {
// Test code
}
Test Class Naming
// ClassNameTest
public class UserServiceTest {
// Test methods
}
public class OrderControllerTest {
// Test methods
}
Test Data Management
Test Fixtures
public class TestFixtures {
public static User createTestUser() {
return new User(
"test@example.com",
"password123",
"Test User"
);
}
public static Order createTestOrder() {
return new Order(
createTestUser(),
Arrays.asList(
new OrderItem("Product 1", 2, 10.0),
new OrderItem("Product 2", 1, 20.0)
)
);
}
}
Test Data Cleanup
@AfterEach
void cleanup() {
// Clean up test data
userRepository.deleteAll();
orderRepository.deleteAll();
}
@AfterAll
void cleanupAll() {
// Clean up any remaining resources
testDatabase.close();
}
Assertion Best Practices
Using Assertions Effectively
// Good assertions
@Test
void processOrder_ValidOrder_UpdatesInventory() {
// Given
Order order = createTestOrder();
int initialStock = productRepository.getStock(order.getItems().get(0).getProductId());
// When
orderService.processOrder(order);
// Then
int finalStock = productRepository.getStock(order.getItems().get(0).getProductId());
assertEquals(initialStock - 2, finalStock, "Stock should be reduced by order quantity");
assertTrue(order.getStatus() == OrderStatus.PROCESSED, "Order should be marked as processed");
}
Custom Assertions
public class OrderAssertions {
public static void assertValidOrder(Order order) {
assertNotNull(order, "Order should not be null");
assertNotNull(order.getUser(), "Order should have a user");
assertFalse(order.getItems().isEmpty(), "Order should have items");
assertTrue(order.getTotalAmount() > 0, "Order should have a positive total");
}
}
Mocking Best Practices
Mock Configuration
@ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
@Mock
private PaymentGateway paymentGateway;
@Mock
private InventoryService inventoryService;
@InjectMocks
private OrderService orderService;
@BeforeEach
void setup() {
when(paymentGateway.processPayment(anyDouble()))
.thenReturn(true);
when(inventoryService.checkStock(anyString()))
.thenReturn(10);
}
}
Verifying Mock Interactions
@Test
void processOrder_ValidOrder_UpdatesInventoryAndProcessesPayment() {
// Given
Order order = createTestOrder();
// When
orderService.processOrder(order);
// Then
verify(paymentGateway).processPayment(order.getTotalAmount());
verify(inventoryService).updateStock(
eq(order.getItems().get(0).getProductId()),
eq(-2)
);
}
Performance Testing
Test Execution Time
@Test
@Timeout(5) // Test should complete within 5 seconds
void processLargeOrder_ShouldCompleteWithinTimeLimit() {
// Test code
}
@Test
@DisabledIf("isSlowEnvironment")
void performanceTest_ShouldRunOnlyInFastEnvironment() {
// Test code
}
Load Testing
@RepeatedTest(100)
void processOrder_UnderLoad_ShouldHandleRequests() {
// Test code
}
@Test
void processMultipleOrders_ShouldHandleConcurrentRequests() {
// Test code with concurrent execution
}