单元测试指南

单元测试是软件开发中针对程序模块(最小单元)进行的正确性检验。在 Java 中,我们通常使用 JUnit 框架来编写和运行单元测试。以下是一个详细的指南:

1. 引入依赖

首先,需要在项目中引入 JUnit 的依赖。如果你使用 Maven,在 pom.xml 文件中添加:

xml 复制代码
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.0</version> <!-- 使用最新稳定版 -->
    <scope>test</scope>
</dependency>

2. 创建被测类

假设我们有一个简单的字符串工具类:

java 复制代码
public class StringUtils {
    // 反转字符串
    public static String reverse(String input) {
        if (input == null) {
            return null;
        }
        return new StringBuilder(input).reverse().toString();
    }

    // 判断字符串是否为空
    public static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }
}

3. 创建测试类

src/test/java 目录下创建对应的测试类:

java 复制代码
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class StringUtilsTest {

    @Test
    void testReverse() {
        // 正常情况
        assertEquals("cba", StringUtils.reverse("abc"));
        
        // 空字符串
        assertEquals("", StringUtils.reverse(""));
        
        // null 值
        assertNull(StringUtils.reverse(null));
    }

    @Test
    void testIsEmpty() {
        assertTrue(StringUtils.isEmpty(null));    // null
        assertTrue(StringUtils.isEmpty(""));      // 空字符串
        assertTrue(StringUtils.isEmpty("  "));   // 空白字符
        assertFalse(StringUtils.isEmpty("hello")); // 非空字符串
    }
}

4. 常用注解

  • @Test:标记方法为测试方法
  • @BeforeEach:每个测试方法执行
  • @AfterEach:每个测试方法执行
  • @BeforeAll:所有测试方法执行(静态方法)
  • @AfterAll:所有测试方法执行(静态方法)
  • @Disabled:禁用测试方法
java 复制代码
@BeforeEach
void setup() {
    // 初始化测试资源
}

@AfterEach
void teardown() {
    // 清理资源
}

5. 断言方法

  • assertEquals(expected, actual):验证相等
  • assertTrue(condition):验证为真
  • assertFalse(condition):验证为假
  • assertNull(object):验证为 null
  • assertNotNull(object):验证非 null
  • assertThrows(Exception.class, () -> {}):验证抛出异常

6. 参数化测试

使用 @ParameterizedTest 简化多数据测试:

java 复制代码
@ParameterizedTest
@ValueSource(strings = {"", "  ", "test"})
void testIsEmptyWithParams(String input) {
    boolean expected = input == null || input.trim().isEmpty();
    assertEquals(expected, StringUtils.isEmpty(input));
}

7. Mockito 模拟依赖

当测试需要隔离外部依赖时,使用 Mockito:

java 复制代码
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private PaymentGateway paymentGateway;

    @InjectMocks
    private OrderService orderService;

    @Test
    void testPlaceOrder() {
        // 设置模拟行为
        when(paymentGateway.process(anyDouble())).thenReturn(true);
        
        Order order = new Order(100.0);
        assertTrue(orderService.placeOrder(order));
        
        // 验证模拟调用
        verify(paymentGateway).process(100.0);
    }
}

8. 测试命名规范

建议使用 MethodName_StateUnderTest_ExpectedBehavior 模式:

java 复制代码
@Test
void reverse_NullInput_ReturnsNull() {
    assertNull(StringUtils.reverse(null));
}

9. 测试覆盖率

使用 Jacoco 生成覆盖率报告(Maven 配置示例):

xml 复制代码
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.10</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>generate-report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

10. 最佳实践

  1. 测试代码保持与生产代码同等质量
  2. 每个测试方法只验证一个行为
  3. 使用 Given-When-Then 结构:
java 复制代码
@Test
void shouldReturnNullWhenInputIsNull() {
    // Given
    String input = null;
    
    // When
    String result = StringUtils.reverse(input);
    
    // Then
    assertNull(result);
}
  1. 避免测试私有方法(通过公共方法间接测试)
  2. 定期运行测试(建议集成到 CI/CD 流程)
相关推荐
金銀銅鐵1 天前
浅解 JUnit 4 第十一篇:@Before 注解和 @After 注解如何发挥作用?
junit·单元测试
jiayou641 天前
KingbaseES 实战:深度解析数据库对象访问权限管理
数据库
李广坤2 天前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
金銀銅鐵2 天前
浅解 JUnit 4 第十篇:方法上的 @Ignore 注解
junit·单元测试
爱可生开源社区3 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1773 天前
《从零搭建NestJS项目》
数据库·typescript
加号34 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏4 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐4 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再4 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip