晴天技术
AI7 min read

AI 辅助测试:用 AI 自动生成单元测试和集成测试

AI 辅助测试:用 AI 自动生成单元测试和集成测试

AI测试单元测试自动化测试Java

写测试是开发者最不喜欢的工作之一。AI 可以自动生成高质量的测试代码。

为什么测试重要但被忽视?

开发者不写测试的原因

  • 写测试耗时
  • 测试代码枯燥
  • 赶进度优先
  • 觉得没必要

AI 改变了一切

  • AI 生成测试只需几秒
  • AI 能覆盖边界情况
  • AI 能生成大量测试用例
  • 开发者只需审查

AI 生成单元测试

Java + JUnit 5

Prompt:为以下 Java 方法生成单元测试:
- 使用 JUnit 5 + Mockito
- 覆盖正常场景、边界值、异常场景
- 测试方法名用英文,清晰描述测试意图

public class OrderService {
    public Order createOrder(Long userId, List<OrderItem> items) {
        if (items == null || items.isEmpty()) {
            throw new BusinessException("订单商品不能为空");
        }
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new BusinessException("用户不存在"));
        BigDecimal total = items.stream()
            .map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        Order order = new Order();
        order.setUserId(userId);
        order.setTotal(total);
        order.setStatus("CREATED");
        return orderRepository.save(order);
    }
}

AI 会生成:

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private UserRepository userRepository;
    @Mock
    private OrderRepository orderRepository;
    @InjectMocks
    private OrderService orderService;

    @Test
    void createOrder_Success() {
        Long userId = 1L;
        List<OrderItem> items = List.of(
            new OrderItem("商品A", new BigDecimal("100"), 2)
        );
        User user = new User(userId, "testuser");
        when(userRepository.findById(userId)).thenReturn(Optional.of(user));
        when(orderRepository.save(any(Order.class))).thenAnswer(i -> i.getArgument(0));

        Order result = orderService.createOrder(userId, items);

        assertNotNull(result);
        assertEquals(userId, result.getUserId());
        assertEquals(new BigDecimal("200"), result.getTotal());
        assertEquals("CREATED", result.getStatus());
    }

    @Test
    void createOrder_EmptyItems_ThrowsException() {
        assertThrows(BusinessException.class,
            () -> orderService.createOrder(1L, Collections.emptyList()));
    }

    @Test
    void createOrder_NullItems_ThrowsException() {
        assertThrows(BusinessException.class,
            () -> orderService.createOrder(1L, null));
    }

    @Test
    void createOrder_UserNotFound_ThrowsException() {
        when(userRepository.findById(99L)).thenReturn(Optional.empty());
        assertThrows(BusinessException.class,
            () -> orderService.createOrder(99L, List.of(new OrderItem())));
    }
}

Python + pytest

Prompt:为以下 Python 函数生成 pytest 测试:

def calculate_discount(price: float, membership: str) -> float:
    if price < 0:
        raise ValueError("价格不能为负数")
    if membership == "gold":
        return price * 0.8
    elif membership == "silver":
        return price * 0.9
    else:
        return price

AI 会生成:

import pytest
from discount import calculate_discount

def test_gold_member_discount():
    assert calculate_discount(100, "gold") == 80

def test_silver_member_discount():
    assert calculate_discount(100, "silver") == 90

def test_normal_member_no_discount():
    assert calculate_discount(100, "normal") == 100

def test_zero_price():
    assert calculate_discount(0, "gold") == 0

def test_negative_price_raises():
    with pytest.raises(ValueError):
        calculate_discount(-10, "gold")

def test_large_price():
    assert calculate_discount(1000000, "gold") == 800000

AI 生成集成测试

Prompt:为以下 Spring Boot Controller 生成集成测试:
- 使用 @SpringBootTest
- 使用 MockMvc
- 测试所有接口的正常和异常场景

@RestController
@RequestMapping("/api/users")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public User createUser(@Valid @RequestBody CreateUserRequest request) {
        return userService.createUser(request);
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @Valid @RequestBody UpdateUserRequest request) {
        return userService.updateUser(id, request);
    }

    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

AI 生成 API 测试

Prompt:为以下 API 生成测试用例,用表格形式列出:

POST /api/orders
请求体:{ userId, items: [{productId, quantity}] }
返回:{ orderId, total, status }

列出所有需要测试的场景,包括正常和异常情况。

AI 会生成:

场景请求预期结果
正常创建有效数据201, 返回订单
商品为空items: []400, 错误信息
用户不存在无效 userId404, 用户不存在
库存不足超量购买400, 库存不足
未登录无 token401, 未授权

Cursor 生成测试

在 Cursor 中,选中代码后:

  1. Cmd+K 输入 "Generate unit tests"
  2. 或者用 Chat:/tests

Cursor 会自动生成测试代码并插入到文件中。

Claude Code 生成测试

claude > 为 src/service/OrderService.java 生成单元测试
claude > 运行测试并修复失败的用例

测试覆盖率提升

Prompt 模板

分析以下代码的测试覆盖率,找出未覆盖的分支:
[粘贴代码和现有测试]

为每个未覆盖的分支生成测试用例。

自动生成边界测试

为以下方法生成边界值测试:
- 空值
- 零值
- 最大值
- 最小值
- 特殊字符
- 并发场景

[粘贴方法代码]

测试数据生成

生成测试数据

生成 10 条用户测试数据,包含:
- 正常用户
- 边界情况(超长名字、特殊字符)
- 异常情况(空值、无效邮箱)

用 JSON 格式输出。

生成 SQL 测试数据

为 users 表生成 SQL INSERT 测试数据:
- 5 条正常数据
- 2 条边界数据
- 包含 NULL 值

表结构:id, name, email, age, created_at

测试最佳实践

1. AI 生成 + 人工审查

AI 生成的测试需要审查:

  • 测试逻辑是否正确
  • 是否覆盖关键场景
  • 断言是否合理

2. 测试命名规范

// 好的命名
@Test
void createUser_WithValidData_ShouldReturnCreatedUser()

// 差的命名
@Test
void test1()

3. 测试独立性

每个测试应该独立运行,不依赖其他测试。

4. 测试可读性

测试代码要清晰易读,像文档一样。

工具推荐

工具特点适合
CursorIDE 集成日常开发
Claude Code命令行批量生成
Diffblue CoverJava 专用Java 项目
CodiumAI多语言团队使用

总结

AI 辅助测试的价值:

  • 效率提升:生成测试从小时级到秒级
  • 覆盖率提高:AI 能想到更多边界情况
  • 质量提升:测试更全面
  • 成本降低:减少测试编写时间

关键:AI 生成,人工审查,持续运行