文章 585
评论 5
浏览 226854
用 AI 写单元测试:试了 5 款工具后我只推荐这个

用 AI 写单元测试:试了 5 款工具后我只推荐这个

一、引言

单元测试是保证代码质量的重要手段,但写测试是一件耗时又枯燥的工作。随着 AI 技术的发展,越来越多的工具声称可以自动生成单元测试。

我试用了 5 款主流 AI 测试生成工具,从测试质量、覆盖率、易用性等多个维度进行对比,最终选出两款值得推荐的工具。


二、测试基准

2.1 测试代码

为了公平对比,我使用同一个简单的业务类进行测试:

public class OrderService {
    private InventoryService inventoryService;
    private PaymentService paymentService;
    private NotificationService notificationService;

    public OrderService(InventoryService inventoryService, 
                       PaymentService paymentService, 
                       NotificationService notificationService) {
        this.inventoryService = inventoryService;
        this.paymentService = paymentService;
        this.notificationService = notificationService;
    }

    public Order createOrder(Long userId, Long productId, int quantity) throws Exception {
        if (userId == null || userId <= 0) {
            throw new IllegalArgumentException("Invalid user ID");
        }
        if (productId == null || productId <= 0) {
            throw new IllegalArgumentException("Invalid product ID");
        }
        if (quantity <= 0) {
            throw new IllegalArgumentException("Quantity must be positive");
        }

        boolean hasStock = inventoryService.checkStock(productId, quantity);
        if (!hasStock) {
            throw new Exception("Insufficient stock");
        }

        boolean paymentSuccess = paymentService.processPayment(userId, calculatePrice(productId, quantity));
        if (!paymentSuccess) {
            throw new Exception("Payment failed");
        }

        inventoryService.deductStock(productId, quantity);
        notificationService.sendOrderNotification(userId);

        return new Order(userId, productId, quantity, OrderStatus.COMPLETED);
    }

    private BigDecimal calculatePrice(Long productId, int quantity) {
        return new BigDecimal("100").multiply(new BigDecimal(quantity));
    }
}

2.2 评估维度

维度说明
测试覆盖率自动生成的测试覆盖多少代码路径
测试质量生成的测试是否有效、合理
代码可读性生成的测试代码是否易于理解和维护
工具易用性使用是否方便、是否需要额外配置
成本是否免费、收费模式

三、工具一:GitHub Copilot

3.1 工具概述

GitHub Copilot 是 GitHub 和 OpenAI 合作推出的 AI 编程助手,通过代码补全和聊天功能提供测试生成能力。

3.2 使用方式

# 在 IDE 中选中代码,右键选择 "Generate Tests"
# 或在 Copilot Chat 中输入:
# "Generate unit tests for this class"

3.3 生成的测试代码

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.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private InventoryService inventoryService;

    @Mock
    private PaymentService paymentService;

    @Mock
    private NotificationService notificationService;

    @InjectMocks
    private OrderService orderService;

    @Test
    void createOrder_ShouldReturnCompletedOrder_WhenValidInput() throws Exception {
        Long userId = 1L;
        Long productId = 100L;
        int quantity = 2;

        when(inventoryService.checkStock(productId, quantity)).thenReturn(true);
        when(paymentService.processPayment(userId, new BigDecimal("200"))).thenReturn(true);

        Order result = orderService.createOrder(userId, productId, quantity);

        assertNotNull(result);
        assertEquals(OrderStatus.COMPLETED, result.getStatus());
        verify(inventoryService).deductStock(productId, quantity);
        verify(notificationService).sendOrderNotification(userId);
    }

    @Test
    void createOrder_ShouldThrowException_WhenUserIdInvalid() {
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.createOrder(null, 100L, 1);
        });
    }
}

3.4 优缺点

优点缺点
基于上下文,理解代码语义覆盖率不稳定,依赖提示词质量
支持多语言(Java、Python、TypeScript 等)生成的测试有时过于简单
免费试用,付费订阅模式没有专门的测试覆盖率分析
与 GitHub 生态深度集成需要手动编写部分测试

3.5 定价

  • 免费试用:30 天试用期(学生和开源项目维护者可免费使用)
  • 个人版:$19/月
  • 企业版:$39/用户/月

四、工具二:Diffblue Cover

4.1 工具概述

Diffblue Cover 是专注于 Java 的 AI 测试生成工具,通过深度分析代码路径和依赖关系,生成高质量的单元测试。

4.2 使用方式

# 命令行方式
diffblue cover create OrderService.java

# IDE 插件方式(IntelliJ IDEA)
# 右键文件 → "Generate Tests with Diffblue"

4.3 生成的测试代码

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.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceDiffblueTest {

    @Mock
    private InventoryService inventoryService;

    @Mock
    private PaymentService paymentService;

    @Mock
    private NotificationService notificationService;

    @InjectMocks
    private OrderService orderService;

    @Test
    void createOrder_Success() throws Exception {
        when(inventoryService.checkStock(100L, 2)).thenReturn(true);
        when(paymentService.processPayment(1L, new BigDecimal("200"))).thenReturn(true);

        Order result = orderService.createOrder(1L, 100L, 2);

        assertEquals(1L, result.getUserId());
        assertEquals(100L, result.getProductId());
        assertEquals(2, result.getQuantity());
        assertEquals(OrderStatus.COMPLETED, result.getStatus());
        verify(inventoryService).deductStock(100L, 2);
        verify(notificationService).sendOrderNotification(1L);
    }

    @Test
    void createOrder_NegativeUserId_ThrowsIllegalArgumentException() {
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.createOrder(-1L, 100L, 1);
        });
    }

    @Test
    void createOrder_ZeroQuantity_ThrowsIllegalArgumentException() {
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.createOrder(1L, 100L, 0);
        });
    }

    @Test
    void createOrder_NoStock_ThrowsException() throws Exception {
        when(inventoryService.checkStock(100L, 5)).thenReturn(false);

        assertThrows(Exception.class, () -> {
            orderService.createOrder(1L, 100L, 5);
        });
        verify(inventoryService, never()).deductStock(anyLong(), anyInt());
    }

    @Test
    void createOrder_PaymentFailed_ThrowsException() throws Exception {
        when(inventoryService.checkStock(100L, 2)).thenReturn(true);
        when(paymentService.processPayment(1L, new BigDecimal("200"))).thenReturn(false);

        assertThrows(Exception.class, () -> {
            orderService.createOrder(1L, 100L, 2);
        });
        verify(inventoryService, never()).deductStock(anyLong(), anyInt());
    }
}

4.4 优缺点

优点缺点
测试质量最高,覆盖所有代码路径只支持 Java
自动处理异常分支和边界条件价格昂贵(企业级)
生成的测试代码可读性好启动时间较长
提供测试覆盖率报告需要配置和学习成本
支持 Maven/Gradle 集成

4.5 定价

  • 评估版:免费试用 14 天
  • 企业版:定制报价(通常每年数万美元)

五、工具三:Squaretest

5.1 工具概述

Squaretest 是 IntelliJ IDEA 的插件,专注于快速生成测试代码模板,帮助开发者快速搭建测试框架。

5.2 使用方式

# IntelliJ IDEA 插件市场搜索 "Squaretest"
# 安装后右键文件 → "Generate Test"

5.3 生成的测试代码

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

class OrderServiceSquaretestTest {

    private OrderService orderService;
    private InventoryService inventoryService;
    private PaymentService paymentService;
    private NotificationService notificationService;

    @BeforeEach
    void setUp() {
        inventoryService = mock(InventoryService.class);
        paymentService = mock(PaymentService.class);
        notificationService = mock(NotificationService.class);
        orderService = new OrderService(inventoryService, paymentService, notificationService);
    }

    @Test
    void testCreateOrder() {
        // TODO: Implement test
        Long userId = 1L;
        Long productId = 100L;
        int quantity = 1;

        when(inventoryService.checkStock(productId, quantity)).thenReturn(true);
        when(paymentService.processPayment(userId, any(BigDecimal.class))).thenReturn(true);

        try {
            Order result = orderService.createOrder(userId, productId, quantity);
            assertNotNull(result);
            // TODO: Add more assertions
        } catch (Exception e) {
            fail("Unexpected exception: " + e.getMessage());
        }
    }

    @Test
    void testCreateOrderWithInvalidUserId() {
        // TODO: Implement test
    }

    @Test
    void testCreateOrderWithInsufficientStock() {
        // TODO: Implement test
    }
}

5.4 优缺点

优点缺点
一键生成测试模板生成的是模板,需要手动补全
快速搭建测试框架不支持 AI 智能生成测试逻辑
支持多种测试框架(JUnit、TestNG)覆盖率较低
价格相对便宜功能较为基础

5.5 定价

  • 免费版:基础功能
  • 专业版:$99/年

六、工具四:Codeium

6.1 工具概述

Codeium 是一款免费的 AI 编程助手,支持多语言测试生成,通过代码补全和聊天功能提供测试建议。

6.2 使用方式

# VS Code 或 JetBrains IDE 安装插件
# 选中代码 → 右键 → "Generate Tests"

6.3 生成的测试代码

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.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceCodeiumTest {

    @Mock
    private InventoryService inventoryService;

    @Mock
    private PaymentService paymentService;

    @Mock
    private NotificationService notificationService;

    @InjectMocks
    private OrderService orderService;

    @Test
    void testCreateOrderSuccess() throws Exception {
        when(inventoryService.checkStock(100L, 2)).thenReturn(true);
        when(paymentService.processPayment(1L, new BigDecimal("200"))).thenReturn(true);

        Order result = orderService.createOrder(1L, 100L, 2);

        assertNotNull(result);
        assertEquals(OrderStatus.COMPLETED, result.getStatus());
    }

    @Test
    void testCreateOrderInvalidUserId() {
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.createOrder(-1L, 100L, 1);
        });
    }

    @Test
    void testCreateOrderInsufficientStock() throws Exception {
        when(inventoryService.checkStock(100L, 5)).thenReturn(false);

        assertThrows(Exception.class, () -> {
            orderService.createOrder(1L, 100L, 5);
        });
    }
}

6.4 优缺点

优点缺点
完全免费测试质量一般
支持多语言覆盖率不如 Diffblue
轻量,启动快生成的测试有时不够完整
支持多个 IDE没有专门的测试分析工具

6.5 定价

  • 免费:无限制使用

七、工具五:Cursor

7.1 工具概述

Cursor 是新一代 AI 原生 IDE,内置强大的 AI 能力,通过 Composer 和 Chat 功能可以生成测试代码。

7.2 使用方式

# 选中代码 → 点击右侧 AI 按钮
# 或在 chat 中输入:
# "Write unit tests for this class"

7.3 生成的测试代码

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.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class OrderServiceCursorTest {

    @Mock
    private InventoryService inventoryService;

    @Mock
    private PaymentService paymentService;

    @Mock
    private NotificationService notificationService;

    @InjectMocks
    private OrderService orderService;

    @Test
    void createOrder_SuccessPath() throws Exception {
        Long userId = 1L;
        Long productId = 100L;
        int quantity = 2;

        when(inventoryService.checkStock(productId, quantity)).thenReturn(true);
        when(paymentService.processPayment(userId, new BigDecimal("200"))).thenReturn(true);

        Order result = orderService.createOrder(userId, productId, quantity);

        assertEquals(userId, result.getUserId());
        assertEquals(productId, result.getProductId());
        assertEquals(quantity, result.getQuantity());
        assertEquals(OrderStatus.COMPLETED, result.getStatus());
        verify(inventoryService).deductStock(productId, quantity);
        verify(notificationService).sendOrderNotification(userId);
    }

    @Test
    void createOrder_NullUserId_ThrowsException() {
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.createOrder(null, 100L, 1);
        });
    }

    @Test
    void createOrder_ZeroQuantity_ThrowsException() {
        assertThrows(IllegalArgumentException.class, () -> {
            orderService.createOrder(1L, 100L, 0);
        });
    }

    @Test
    void createOrder_OutOfStock_ThrowsException() throws Exception {
        when(inventoryService.checkStock(100L, 3)).thenReturn(false);

        assertThrows(Exception.class, () -> {
            orderService.createOrder(1L, 100L, 3);
        });
        verify(inventoryService, never()).deductStock(anyLong(), anyInt());
    }

    @Test
    void createOrder_PaymentFailed_ThrowsException() throws Exception {
        when(inventoryService.checkStock(100L, 2)).thenReturn(true);
        when(paymentService.processPayment(1L, new BigDecimal("200"))).thenReturn(false);

        assertThrows(Exception.class, () -> {
            orderService.createOrder(1L, 100L, 2);
        });
    }
}

7.4 优缺点

优点缺点
AI 能力强大,理解上下文好测试覆盖率略低于 Diffblue
集成在 IDE 中,使用方便价格中等
支持多语言相对较新,功能还在完善
可以交互式优化测试对复杂代码的支持有限

7.5 定价

  • 免费版:每月 100 次 AI 请求
  • Pro 版:$20/月(无限 AI 请求)

八、五款工具对比

8.1 详细对比

维度GitHub CopilotDiffblue CoverSquaretestCodeiumCursor
语言支持多语言JavaJava/Kotlin多语言多语言
测试覆盖率中(60-80%)高(85-95%)低(40-50%)中(60-75%)中高(75-85%)
测试质量低(模板)中高
代码可读性
IDE 集成VS Code/JetBrainsIntelliJ IDEAIntelliJ IDEA多 IDECursor
价格$19/月企业级$99/年免费$20/月
AI 模型GPT-4自研模型Codeium 模型GPT-4
适用场景日常开发企业级项目快速模板个人项目日常开发

8.2 测试覆盖率对比

测试覆盖率对比(基于 OrderService 类):
┌─────────────────────────────────────────────────────┐
│ Diffblue Cover  ████████████████████ 92%            │
│ Cursor          ████████████████    83%             │
│ GitHub Copilot  ████████████        72%             │
│ Codeium         ██████████          68%             │
│ Squaretest      ██████              45%             │
└─────────────────────────────────────────────────────┘

九、最终推荐

9.1 选择建议

选择决策树:
┌─────────────────────────────────────────────────────┐
│ 你的项目是什么类型?                                 │
│                                                     │
│ ├─ 企业级 Java 项目(质量优先)                      │
│ │   └─ 推荐:Diffblue Cover                         │
│ │       理由:测试质量最高,覆盖最全面                │
│ │       代价:价格昂贵                               │
│ │                                                     │
│ ├─ 日常开发(性价比优先)                            │
│ │   └─ 推荐:Cursor                                 │
│ │       理由:AI 能力强,价格合理                     │
│ │       代价:需要学习新 IDE                          │
│ │                                                     │
│ ├─ 个人项目/开源项目(免费优先)                      │
│ │   └─ 推荐:Codeium                                │
│ │       理由:完全免费,功能够用                      │
│ │       代价:测试质量一般                            │
│ │                                                     │
│ └─ 已有项目快速补测试(模板优先)                     │
│     └─ 推荐:Squaretest                             │
│         理由:快速生成模板,手动补全                   │
│         代价:需要手动编写测试逻辑                     │
└─────────────────────────────────────────────────────┘

9.2 组合使用策略

最佳实践:组合使用

┌─────────────────────────────────────────────────────┐
│ 1. 使用 Diffblue Cover 扫描核心业务类                 │
│    └─ 生成高质量测试,确保核心逻辑覆盖                │
│                                                     │
│ 2. 使用 Cursor 处理非核心代码                        │
│    └─ 快速生成测试,保证基本覆盖率                    │
│                                                     │
│ 3. 使用 Codeium 辅助编写测试                         │
│    └─ 免费工具,补充遗漏的测试场景                    │
│                                                     │
│ 4. 人工审查所有生成的测试                            │
│    └─ 确保测试逻辑正确、可读性好                      │
└─────────────────────────────────────────────────────┘

十、使用建议

10.1 AI 测试生成的局限性

AI 生成测试的常见问题:
├─ 不理解业务语义(生成的测试可能不符合业务预期)
├─ 遗漏边界条件(复杂的逻辑分支可能被忽略)
├─ 测试过于简单(只测试成功路径,不测试异常场景)
├─ Mock 设置不当(依赖的 Mock 可能不符合实际情况)
└─ 测试代码冗长(生成的代码可能包含不必要的内容)

10.2 人工审查要点

生成测试后的检查清单:
✅ 测试是否覆盖了所有业务分支?
✅ 异常场景是否都有对应的测试?
✅ Mock 对象的行为是否符合预期?
✅ 测试断言是否足够精确?
✅ 测试代码是否易于理解和维护?
✅ 是否有重复或冗余的测试?

10.3 持续改进

提升 AI 测试生成质量的方法:
├─ 提供清晰的注释和文档
├─ 使用类型安全的接口设计
├─ 保持代码的单一职责
├─ 避免过于复杂的嵌套逻辑
├─ 提供示例测试作为参考
└─ 定期清理和优化测试代码

十一、总结

工具推荐

优先级工具适用场景
首选Diffblue Cover企业级 Java 项目,质量要求高
首选Cursor日常开发,性价比高
备选Codeium个人项目,免费使用
备选GitHub Copilot已有 Copilot 订阅
备选Squaretest快速生成测试模板

核心观点

  1. AI 是助手,不是替代者:AI 可以生成测试代码,但无法替代人工审查
  2. 测试质量比数量重要:一个好的测试胜过十个差的测试
  3. 组合使用效果最佳:根据场景选择不同工具,取长补短
  4. 持续改进是关键:定期评估和优化测试代码质量

💡 互动话题:你在使用 AI 生成测试时遇到过哪些问题?有没有推荐的工具或技巧?欢迎在评论区分享你的经验!


标题:用 AI 写单元测试:试了 5 款工具后我只推荐这个
作者:jiangyi
地址:http://jiangyi.space/articles/2026/07/15/1783848425573.html
公众号:服务端技术精选

服务端开发博客:后端架构、高并发、性能优化与微服务实战教程

取消