单元测试指南

单元测试是软件开发中针对程序模块(最小单元)进行的正确性检验。在 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 流程)
相关推荐
悄悄敲敲敲13 小时前
MySQL表的约束
数据库·mysql
鼠爷ねずみ13 小时前
SpringCloud前后端整体开发流程-以及技术总结文章实时更新中
java·数据库·后端·spring·spring cloud
九皇叔叔14 小时前
MySQL 数据库 Read View 详解
数据库·mysql·mvcc·read view
Elastic 中国社区官方博客15 小时前
Elasticsearch:圣诞晚餐 BBQ - 图像识别
大数据·数据库·elasticsearch·搜索引擎·ai·全文检索
cui_win15 小时前
Prometheus实战教程 - Redis 监控
数据库·redis·prometheus
JIngJaneIL16 小时前
基于java + vue个人博客系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
TG:@yunlaoda360 云老大16 小时前
华为云国际站代理商备份策略设置过程中遇到问题如何解决?
服务器·数据库·华为云
SelectDB16 小时前
Doris Catalog 已上线!性能提升 200x,全面优于 JDBC Catalog,跨集群查询迈入高性能分析时代
数据库·数据分析·apache
TAEHENGV16 小时前
进度跟踪模块 Cordova 与 OpenHarmony 混合开发实战
android·javascript·数据库
神秘面具男0316 小时前
MySQL 从基础到实践
数据库·mysql