单元测试指南

单元测试是软件开发中针对程序模块(最小单元)进行的正确性检验。在 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 流程)
相关推荐
JosieBook2 小时前
【数据库】Apache IoTDB数据库在大数据场景下的时序数据模型与建模方案
数据库·apache·iotdb
全栈胖叔叔-瓜州2 小时前
关于微软最新数据库引擎sqlserver2025 关于向量距离函数调用的问题
数据库·microsoft
全栈小52 小时前
【Rust】从0到1开发和运行Web相关功能,并简单实现数据库连接和查询
数据库·rust
墨辰JC2 小时前
基于STM32标准库的FreeRTOS移植与任务创建
数据库·stm32·嵌入式硬件·freertos
R.lin2 小时前
MongoDB知识点与技巧总结
数据库·mongodb
幽水-椰子糖2 小时前
达梦守护搭建
数据库·达梦
q***3753 小时前
Spring Boot 从 2.7.x 升级到 3.3注意事项
数据库·hive·spring boot
王小小鸭3 小时前
【Oracle APEX开发小技巧17】交互式网格操作按钮根据条件/状态设置能否被点击生效
数据库·oracle·oracle apex
lang201509283 小时前
oracle 11查询数据库锁
数据库·oracle