Java 测试:单元测试、集成测试、端到端测试从入门到精通

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么测试是高质量Java代码的基础

每位Java开发人员都遇到过这样的情况:修复一个bug后,又出现了三个新bug。要打破这个恶性循环,就需要测试。这不仅仅是清单上的一个勾选标记,而是一门专业的学科,它能保护你的神经、金钱和产品的声誉。

在本文中,我们将分析三个关键的测试层级:unit(单元测试)、integration(集成测试)和e2e(端到端测试)。我们将通过实际的Java示例,使用流行的框架:JUnit 5、Mockito、Spring Boot Test和Selenium来讲解。你将学习如何构建正确的测试金字塔,并且不会让项目被无用的测试所拖累。

1. 单元测试:验证代码的原子单元

单元测试是最底层、最快的一层。它们验证一个“单元”(通常是一个方法或类)在隔离外部依赖(数据库、API、文件系统)的情况下的工作状态。

1.1 工具和框架

  • JUnit 5 — 在Java中编写测试的事实标准。
  • Mockito — 用于创建模拟对象(mocks)和桩代码(stubs)的库。
  • AssertJ — 用于流畅断言(fluent assertions)的库。

1.2 使用JUnit 5和Mockito的单元测试示例

假设我们有一个OrderService服务,它依赖于PaymentRepository仓库。我们想要测试服务的逻辑,而不调用真实的数据库。

// 被测试的类
public class OrderService {
    private final PaymentRepository paymentRepository;

    public OrderService(PaymentRepository paymentRepository) {
        this.paymentRepository = paymentRepository;
    }

    public String processOrder(Order order) {
        if (order.getAmount() <= 0) {
            throw new IllegalArgumentException("订单金额必须为正数");
        }
        boolean success = paymentRepository.pay(order);
        return success ? "支付成功" : "支付失败";
    }
}

// 单元测试
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private PaymentRepository paymentRepository;

    @InjectMocks
    private OrderService orderService;

    @Test
    void testProcessOrder_Success() {
        // 准备
        Order order = new Order(100.0);
        when(paymentRepository.pay(order)).thenReturn(true);

        // 执行
        String result = orderService.processOrder(order);

        // 断言
        assertEquals("支付成功", result);
        verify(paymentRepository, times(1)).pay(order);
    }

    @Test
    void testProcessOrder_NegativeAmount() {
        // 准备
        Order order = new Order(-10.0);

        // 执行 & 断言
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.processOrder(order);
        });
    }
}

单元测试最佳实践:

  • 一个测试只验证一个点(FIRST原则:快速、隔离、可重复、自我验证、及时)。
  • 使用清晰的命名:shouldReturnSuccess_whenPaymentIsApproved。
  • 不要测试私有方法——测试公共接口。
  • 覆盖边界情况:null、空字符串、负数。

2. 集成测试:验证组件间的交互

集成测试验证系统的不同模块如何

博客

书籍推荐