单元测试指南

单元测试是软件开发中针对程序模块(最小单元)进行的正确性检验。在 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 天前
MySQL JSON_SET() 函数
数据库·mysql·json
山峰哥1 天前
数据库工程与SQL调优——从索引策略到查询优化的深度实践
数据库·sql·性能优化·编辑器
较劲男子汉1 天前
CANN Runtime零拷贝传输技术源码实战 彻底打通Host与Device的数据传输壁垒
运维·服务器·数据库·cann
java搬砖工-苤-初心不变1 天前
MySQL 主从复制配置完全指南:从原理到实践
数据库·mysql
山岚的运维笔记1 天前
SQL Server笔记 -- 第18章:Views
数据库·笔记·sql·microsoft·sqlserver
roman_日积跬步-终至千里1 天前
【LangGraph4j】LangGraph4j 核心概念与图编排原理
java·服务器·数据库
汇智信科1 天前
打破信息孤岛,重构企业效率:汇智信科企业信息系统一体化运营平台
数据库·重构
野犬寒鸦1 天前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
晚霞的不甘1 天前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
市场部需要一个软件开发岗位1 天前
JAVA开发常见安全问题:纵向越权
java·数据库·安全