Skip to main content

Java Testing Frameworks

This guide covers Java testing frameworks, best practices, and strategies for writing effective tests.

🎯 Testing Fundamentals

Testing Pyramid

  • Unit Tests: Fast, isolated tests for individual components
  • Integration Tests: Test component interactions
  • End-to-End Tests: Test complete user workflows
  • Performance Tests: Load and stress testing

Testing Principles

  • AAA Pattern: Arrange, Act, Assert
  • First Principles: Fast, Independent, Repeatable, Self-validating, Timely
  • Test Coverage: Aim for high coverage of critical paths
  • Test Naming: Descriptive test names that explain behavior

🧪 JUnit Framework

JUnit 5 (Jupiter)

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {

private Calculator calculator;

@BeforeEach
void setUp() {
calculator = new Calculator();
}

@Test
@DisplayName("Should add two positive numbers")
void shouldAddTwoPositiveNumbers() {
// Arrange
int a = 5;
int b = 3;

// Act
int result = calculator.add(a, b);

// Assert
assertEquals(8, result, "5 + 3 should equal 8");
}

@Test
@DisplayName("Should throw exception for division by zero")
void shouldThrowExceptionForDivisionByZero() {
// Arrange
int a = 10;
int b = 0;

// Act & Assert
assertThrows(ArithmeticException.class, () -> calculator.divide(a, b));
}

@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
@DisplayName("Should handle positive numbers")
void shouldHandlePositiveNumbers(int number) {
assertTrue(calculator.isPositive(number));
}

@Test
@Disabled("Feature not yet implemented")
@DisplayName("Should calculate square root (disabled)")
void shouldCalculateSquareRoot() {
// Test for future feature
}
}

JUnit 5 Advanced Features

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
import static org.junit.jupiter.api.Assertions.*;

class AdvancedCalculatorTest {

@TestFactory
Stream<DynamicTest> dynamicTestsForPalindrome() {
PalindromeChecker checker = new PalindromeChecker();

return Stream.of("racecar", "level", "radar")
.map(word -> DynamicTest.dynamicTest(
"Test palindrome: " + word,
() -> assertTrue(checker.isPalindrome(word))
));
}

@ParameterizedTest
@CsvSource({
"2, 3, 6",
"0, 5, 0",
"-2, 3, -6"
})
@DisplayName("Should multiply numbers")
void shouldMultiplyNumbers(int a, int b, int expected) {
Calculator calculator = new Calculator();
assertEquals(expected, calculator.multiply(a, b));
}

@RepeatedTest(5)
@DisplayName("Should handle random operations")
void shouldHandleRandomOperations(RepetitionInfo repetitionInfo) {
Calculator calculator = new Calculator();
int random1 = (int) (Math.random() * 100);
int random2 = (int) (Math.random() * 100);

int result = calculator.add(random1, random2);
assertEquals(random1 + random2, result);
}
}

🎭 Mockito Framework

Mockito Basics

import org.junit.jupiter.api.Test;
import org.mockito.*;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class UserServiceTest {

@Mock
private UserRepository userRepository;

@Mock
private EmailService emailService;

@InjectMocks
private UserService userService;

@Test
void shouldCreateUserAndSendEmail() {
// Arrange
User user = new User("[email protected]", "password");
when(userRepository.save(any(User.class))).thenReturn(user);
doNothing().when(emailService).sendWelcomeEmail(user.getEmail());

// Act
User createdUser = userService.createUser(user.getEmail(), user.getPassword());

// Assert
assertNotNull(createdUser);
assertEquals("[email protected]", createdUser.getEmail());

// Verify interactions
verify(userRepository).save(user);
verify(emailService).sendWelcomeEmail(user.getEmail());
}

@Test
void shouldThrowExceptionWhenEmailAlreadyExists() {
// Arrange
String email = "[email protected]";
when(userRepository.existsByEmail(email)).thenReturn(true);

// Act & Assert
assertThrows(UserAlreadyExistsException.class,
() -> userService.createUser(email, "password"));

// Verify no save or email sent
verify(userRepository, never()).save(any(User.class));
verify(emailService, never()).sendWelcomeEmail(anyString());
}
}

Mockito Advanced

import org.junit.jupiter.api.Test;
import org.mockito.*;
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
import static org.junit.jupiter.api.Assertions.*;

class AdvancedUserServiceTest {

@Mock
private UserRepository userRepository;

@Captor
private ArgumentCaptor<User> userCaptor;

@Test
void shouldCreateUserWithCorrectData() {
// Arrange
UserService userService = new UserService(userRepository);
String email = "[email protected]";
String password = "password123";

// Act
userService.createUser(email, password);

// Assert
verify(userRepository).save(userCaptor.capture());
User savedUser = userCaptor.getValue();

assertEquals(email, savedUser.getEmail());
assertNotNull(savedUser.getPasswordHash());
assertNotEquals(password, savedUser.getPasswordHash()); // Should be hashed
}

@Test
void shouldHandleRepositoryException() {
// Arrange
when(userRepository.save(any(User.class)))
.thenThrow(new DatabaseException("Connection failed"));

UserService userService = new UserService(userRepository);

// Act & Assert
assertThrows(UserCreationException.class,
() -> userService.createUser("[email protected]", "password"));
}
}

🌐 Spring Testing

Spring Boot Testing

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringBootTest
@AutoConfigureTestDatabase
class UserControllerIntegrationTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private EmailService emailService;

@Test
void shouldCreateUser() throws Exception {
String userJson = "{\"email\":\"[email protected]\",\"password\":\"password\"}";

mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.email").value("[email protected]"));

verify(emailService).sendWelcomeEmail("[email protected]");
}
}

Data JPA Testing

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import static org.junit.jupiter.api.Assertions.*;

@DataJpaTest
class UserRepositoryTest {

@Autowired
private TestEntityManager entityManager;

@Autowired
private UserRepository userRepository;

@Test
void shouldFindUserByEmail() {
// Arrange
User user = new User("[email protected]", "hashedPassword");
entityManager.persistAndFlush(user);

// Act
Optional<User> found = userRepository.findByEmail("[email protected]");

// Assert
assertTrue(found.isPresent());
assertEquals("[email protected]", found.get().getEmail());
}

@Test
void shouldReturnEmptyWhenUserNotFound() {
// Act
Optional<User> found = userRepository.findByEmail("[email protected]");

// Assert
assertFalse(found.isPresent());
}
}

🚀 Testcontainers

Database Testing with Testcontainers

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase;

@Testcontainers
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserRepositoryIntegrationTest {

@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");

@Autowired
private UserRepository userRepository;

@Test
void shouldCreateAndFindUser() {
// Arrange
User user = new User("[email protected]", "hashedPassword");

// Act
User saved = userRepository.save(user);
Optional<User> found = userRepository.findByEmail("[email protected]");

// Assert
assertNotNull(saved.getId());
assertTrue(found.isPresent());
assertEquals("[email protected]", found.get().getEmail());
}
}

📊 Performance Testing

JMH for Microbenchmarks

import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
public class StringConcatenationBenchmark {

private String[] strings;

@Setup
public void setUp() {
strings = new String[1000];
for (int i = 0; i < strings.length; i++) {
strings[i] = "string" + i;
}
}

@Benchmark
public String concatenateWithPlus() {
String result = "";
for (String s : strings) {
result += s;
}
return result;
}

@Benchmark
public String concatenateWithStringBuilder() {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append(s);
}
return sb.toString();
}

@Benchmark
public String concatenateWithStringJoin() {
return String.join("", strings);
}
}

🛠️ Testing Best Practices

Test Structure

class WellStructuredTest {

// 1. Clear test name
@Test
@DisplayName("Should calculate discount for premium customer")
void shouldCalculateDiscountForPremiumCustomer() {

// 2. Arrange - set up test data
Customer customer = new Customer();
customer.setPremium(true);
Order order = new Order(100.0);

// 3. Act - execute the method under test
DiscountCalculator calculator = new DiscountCalculator();
double discount = calculator.calculateDiscount(customer, order);

// 4. Assert - verify the result
assertEquals(20.0, discount, "Premium customer should get 20% discount");
}
}

Test Data Management

// Use builders for test data
public class UserBuilder {
private String email = "[email protected]";
private String password = "password";
private boolean active = true;

public static UserBuilder aUser() {
return new UserBuilder();
}

public UserBuilder withEmail(String email) {
this.email = email;
return this;
}

public UserBuilder withPassword(String password) {
this.password = password;
return this;
}

public User build() {
return new User(email, password, active);
}
}

// Usage in tests
@Test
void shouldCreateActiveUser() {
User user = UserBuilder.aUser()
.withEmail("[email protected]")
.build();

assertTrue(user.isActive());
}

🔗 Language-Specific Testing

📍 Visit Us

🏫 VD Computer Tuition Surat

VD Computer Tuition
📍 Address
2/66 Faram Street, Rustompura
Surat395002, Gujarat, India
📞 Phone / WhatsApp
+91 84604 41384
🌐 Website

Computer Classes & Tuition — Areas We Serve in Surat

AdajanAlthanAmroliAthwaAthwalinesBhagalBhatarBhestanCanal RoadChowkCitylightDumasGaurav PathGhod Dod RoadHaziraJahangirpuraKamrejKapodraKatargamLimbayatMagdallaMajura GateMota VarachhaNanpuraNew CitylightOlpadPalPandesaraParle PointPiplodPunaRanderRing RoadRustampuraSachinSalabatpuraSarthanaSosyo CircleUdhnaVarachhaVed RoadVesuVIP Road
📞 Call Sir💬 WhatsApp Sir