本文目录导读:

我来详细介绍Java单元测试的编写方法和最佳实践。
单元测试基本结构
示例代码:待测试的类
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("除数不能为0");
}
return a / b;
}
public boolean isEven(int number) {
return number % 2 == 0;
}
}
对应的单元测试
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@AfterEach
void tearDown() {
calculator = null;
}
@Test
@DisplayName("测试加法运算")
void testAdd() {
int result = calculator.add(5, 3);
assertEquals(8, result, "5 + 3 应该等于 8");
}
@Test
@DisplayName("测试除法运算")
void testDivide() {
int result = calculator.divide(10, 2);
assertEquals(5, result);
}
@Test
@DisplayName("测试除零异常")
void testDivideByZero() {
assertThrows(IllegalArgumentException.class,
() -> calculator.divide(10, 0));
}
@Test
@DisplayName("测试偶数判断")
void testIsEven() {
assertTrue(calculator.isEven(4));
assertFalse(calculator.isEven(3));
}
}
常用测试注解
import org.junit.jupiter.api.*;
class TestLifecycleDemo {
@BeforeAll
static void initAll() {
// 在所有测试方法执行前执行一次(必须为static)
System.out.println("初始化测试环境");
}
@AfterAll
static void cleanAll() {
// 在所有测试方法执行后执行一次(必须为static)
System.out.println("清理测试环境");
}
@BeforeEach
void init() {
// 每个测试方法执行前执行
System.out.println("准备测试数据");
}
@AfterEach
void clean() {
// 每个测试方法执行后执行
System.out.println("清理测试数据");
}
@Test
@DisplayName("示例测试方法")
void testExample() {
System.out.println("执行测试");
}
@Test
@Disabled("暂时跳过此测试")
void skippedTest() {
// 被跳过的测试
}
}
断言方法详解
class AssertionDemo {
@Test
void testAssertions() {
// 基本断言
assertEquals(5, 5, "值应该相等");
assertNotEquals(5, 10, "值不应该相等");
// 布尔断言
assertTrue(10 > 5, "条件应该为true");
assertFalse(5 > 10, "条件应该为false");
// 对象断言
String str1 = new String("hello");
String str2 = new String("hello");
assertEquals(str1, str2, "对象内容应该相等");
assertNotSame(str1, str2, "对象引用应该不同");
// 空值断言
String nullStr = null;
assertNull(nullStr, "对象应该为null");
assertNotNull("hello", "对象不应该为null");
// 数组断言
int[] expected = {1, 2, 3};
int[] actual = {1, 2, 3};
assertArrayEquals(expected, actual, "数组应该相等");
// 异常断言
assertThrows(IllegalArgumentException.class, () -> {
throw new IllegalArgumentException("测试异常");
});
// 多断言组合
assertAll("多个断言",
() -> assertEquals(1, 1),
() -> assertTrue(true),
() -> assertNotNull("test")
);
}
}
Mockito模拟测试
// 添加Maven依赖
/*
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
*/
import org.mockito.*;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
// 待测试的服务类
class UserService {
private UserRepository userRepository;
private EmailService emailService;
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public User createUser(String name, String email) {
User user = new User(name, email);
User saved = userRepository.save(user);
emailService.sendWelcomeEmail(email);
return saved;
}
}
// Mockito测试
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testCreateUser() {
// 准备Mock数据
User testUser = new User("张三", "zhangsan@example.com");
when(userRepository.save(any(User.class))).thenReturn(testUser);
// 执行测试
User result = userService.createUser("张三", "zhangsan@example.com");
// 验证结果
assertEquals("张三", result.getName());
assertEquals("zhangsan@example.com", result.getEmail());
// 验证方法调用
verify(userRepository, times(1)).save(any(User.class));
verify(emailService, times(1)).sendWelcomeEmail("zhangsan@example.com");
}
@Test
void testCreateUserException() {
// 模拟异常
when(userRepository.save(any(User.class)))
.thenThrow(new RuntimeException("数据库异常"));
// 验证异常
assertThrows(RuntimeException.class,
() -> userService.createUser("张三", "zhangsan@example.com"));
// 验证邮件不被发送
verify(emailService, never()).sendWelcomeEmail(anyString());
}
}
Spring Boot集成测试
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.beans.factory.annotation.Autowired;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetUser() throws Exception {
mockMvc.perform(get("/api/users/1")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("张三"))
.andExpect(jsonPath("$.email").value("zhangsan@example.com"));
}
@Test
void testCreateUser() throws Exception {
String userJson = "{\"name\":\"李四\",\"email\":\"lisi@example.com\"}";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("李四"));
}
}
测试数据驱动
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
class ParameterizedTestDemo {
@ParameterizedTest
@ValueSource(ints = {2, 4, 6, 8, 10})
void testEvenNumbers(int number) {
Calculator calculator = new Calculator();
assertTrue(calculator.isEven(number));
}
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"10, 20, 30",
"100, 200, 300"
})
void testAddWithMultipleInputs(int a, int b, int expected) {
Calculator calculator = new Calculator();
assertEquals(expected, calculator.add(a, b));
}
@ParameterizedTest
@MethodSource("provideTestData")
void testWithMethodSource(String input, int expected) {
assertEquals(expected, input.length());
}
static Stream<Arguments> provideTestData() {
return Stream.of(
Arguments.of("hello", 5),
Arguments.of("world", 5),
Arguments.of("test", 4)
);
}
}
最佳实践总结
测试命名规范
// 方法命名:方法名_场景_期望结果
@Test
void calculateTotalPrice_whenNoDiscount_shouldReturnOriginalPrice() {
// ...
}
@Test
void calculateTotalPrice_whenVIPCustomer_shouldApplyDiscount() {
// ...
}
测试结构(AAA模式)
@Test
void testSomething() {
// Arrange(准备)
Calculator calculator = new Calculator();
int expected = 10;
// Act(执行)
int actual = calculator.add(5, 5);
// Assert(验证)
assertEquals(expected, actual);
}
注意事项
- 单一职责:每个测试方法只测试一个功能点
- 独立性:测试之间不应相互依赖
- 可重复性:同一测试多次执行结果应一致
- 明确性:测试名称应清晰表达测试意图
- 隔离性:使用Mock隔离外部依赖
- 覆盖率:关注核心逻辑和边界条件的覆盖
这就是Java单元测试的基本写法,实际开发中,建议结合项目的具体需求和框架选择合适的测试策略。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播