Java JUnit测试详解:单元测试与集成测试
Java JUnit测试详解:单元测试与集成测试
概述
JUnit是Java中最流行的单元测试框架。本教程介绍JUnit的使用方法和最佳实践。
1. JUnit基础
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
public class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@AfterEach
void tearDown() {
calculator = null;
}
@Test
void testAdd() {
assertEquals(5, calculator.add(2, 3));
}
@Test
void testSubtract() {
assertEquals(1, calculator.subtract(3, 2));
}
@Test
void testMultiply() {
assertEquals(6, calculator.multiply(2, 3));
}
@Test
void testDivide() {
assertEquals(2.0, calculator.divide(4, 2), 0.001);
}
@Test
void testDivideByZero() {
assertThrows(ArithmeticException.class, () -> {
calculator.divide(1, 0);
});
}
@Test
void testWithTimeout() {
assertTimeout(Duration.ofMillis(100), () -> {
calculator.add(1, 2);
});
}
}
class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
public int multiply(int a, int b) {
return a * b;
}
public double divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("除数不能为零");
}
return (double) a / b;
}
}
2. 参数化测试
import org.junit.jupiter.params.*;
import org.junit.jupiter.params.provider.*;
import static org.junit.jupiter.api.Assertions.*;
public class ParameterizedTest {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsPositive(int number) {
assertTrue(number > 0);
}
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"4, 5, 9",
"10, 20, 30"
})
void testAdd(int a, int b, int expected) {
assertEquals(expected, a + b);
}
@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv", numLinesToSkip = 1)
void testWithCsvFile(String input, String expected) {
assertEquals(expected, input.toUpperCase());
}
@ParameterizedTest
@MethodSource("provideStringsForIsBlank")
void testIsBlank(String input, boolean expected) {
assertEquals(expected, input.isBlank());
}
static Stream<Arguments> provideStringsForIsBlank() {
return Stream.of(
Arguments.of("", true),
Arguments.of(" ", true),
Arguments.of("not blank", false)
);
}
}
3. 集成测试
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class UserServiceIntegrationTest {
private UserService userService;
@BeforeAll
static void setUp() {
// 初始化数据库连接等
}
@AfterAll
static void tearDown() {
// 清理资源
}
@BeforeEach
void init() {
userService = new UserService();
}
@Test
@Order(1)
void testCreateUser() {
User user = userService.createUser("Alice", 25);
assertNotNull(user.getId());
assertEquals("Alice", user.getName());
}
@Test
@Order(2)
void testFindUser() {
User user = userService.createUser("Bob", 30);
User found = userService.findById(user.getId());
assertEquals("Bob", found.getName());
}
@Test
@Order(3)
void testDeleteUser() {
User user = userService.createUser("Charlie", 35);
userService.delete(user.getId());
assertNull(userService.findById(user.getId()));
}
}
class UserService {
public User createUser(String name, int age) {
return new User(1L, name, age);
}
public User findById(Long id) {
return new User(id, "Test", 25);
}
public void delete(Long id) {
// 删除用户
}
}
class User {
private Long id;
private String name;
private int age;
public User(Long id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
}
4. Mock测试
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class MockTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testCreateUser() {
User user = new User(1L, "Alice", 25);
when(userRepository.save(any(User.class))).thenReturn(user);
User created = userService.createUser("Alice", 25);
assertNotNull(created);
assertEquals("Alice", created.getName());
verify(userRepository, times(1)).save(any(User.class));
}
@Test
void testFindUser() {
User user = new User(1L, "Bob", 30);
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
User found = userService.findById(1L);
assertEquals("Bob", found.getName());
verify(userRepository, times(1)).findById(1L);
}
}
interface UserRepository {
User save(User user);
Optional<User> findById(Long id);
}
5. 最佳实践
- 遵循AAA模式:Arrange-Act-Assert
- 每个测试只测一个功能
- 使用有意义的测试方法名
- 测试边界条件
- 使用Mock隔离依赖
总结
JUnit是Java单元测试的标准框架。掌握JUnit的使用方法,可以编写高质量的测试代码。在实际编程中,要编写完整的测试用例,确保代码质量。