← 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
}

Video Content