Skip to content

25.4 智慧測試

智慧測試概述

Claude Code 可以自動生成測試程式碼、分析測試覆蓋率、識別測試盲點,並提供測試最佳化建議。透過智慧測試功能,開發者可以快速構建全面的測試套件,提高程式碼質量和可靠性。

自动生成测试

1. 单元测试生成

示例:生成单元测试

使用者請求: "為這個類生成單元測試"

程式碼:

python
    python


    ````python

    python

    # 示例:生成集成测试

    用户请求:
    "为这个用户服务生成集成测试"

    代码:

    class UserService:
        """用户服务"""

        def __init__(self, db):
            self.db = db

        def create_user(self, username, email, password):
            """创建用户"""
            if self._user_exists(username):
                raise ValueError("Username already exists")

            if self._email_exists(email):
                raise ValueError("Email already exists")

            user = User(
                username=username,
                email=email,
                password_hash=self._hash_password(password)
            )

            self.db.session.add(user)
            self.db.session.commit()

            return user

        def get_user(self, user_id):
            """获取用户"""
            user = self.db.session.query(User).filter_by(id=user_id).first()
            if not user:
                raise ValueError("User not found")
            return user

        def update_user(self, user_id, **kwargs):
            """更新用户"""
            user = self.get_user(user_id)

            if 'username' in kwargs:
                if self._user_exists(kwargs['username'], exclude_id=user_id):
                    raise ValueError("Username already exists")
                user.username = kwargs['username']

            if 'email' in kwargs:
                if self._email_exists(kwargs['email'], exclude_id=user_id):
                    raise ValueError("Email already exists")
                user.email = kwargs['email']

            if 'password' in kwargs:
                user.password_hash = self._hash_password(kwargs['password'])

            self.db.session.commit()

            return user

        def delete_user(self, user_id):
            """删除用户"""
            user = self.get_user(user_id)
            self.db.session.delete(user)
            self.db.session.commit()

        def _user_exists(self, username, exclude_id=None):
            """检查用户名是否存在"""
            query = self.db.session.query(User).filter_by(username=username)
            if exclude_id:
                query = query.filter(User.id != exclude_id)
            return query.first() is not None

        def _email_exists(self, email, exclude_id=None):
            """检查邮箱是否存在"""
            query = self.db.session.query(User).filter_by(email=email)
            if exclude_id:
                query = query.filter(User.id != exclude_id)
            return query.first() is not None

        def _hash_password(self, password):
            """哈希密码"""
            import bcrypt
            return bcrypt.hashpw(
                password.encode('utf-8'),
                bcrypt.gensalt()
            ).decode('utf-8')

    Claude Code 生成的测试:

    import unittest
    from unittest.mock import Mock, MagicMock
    from user_service import UserService
    from models import User

    class TestUserService(unittest.TestCase):
    """使用者服務測試類"""

    def setUp(self):
    """測試前設定"""
    self.db = Mock()
    self.db.session = MagicMock()
    self.user_service = UserService(self.db)

    def test_create_user_success(self):
    """測試成功建立使用者"""
    self.db.session.query.return_value.filter_by.return_value.first.return_value = None

    user = self.user_service.create_user(
    username='testuser',
    email='test@example.com',
    password='password123'
    )

    self.db.session.add.assert_called_once()
    self.db.session.commit.assert_called_once()
    self.assertEqual(user.username, 'testuser')
    self.assertEqual(user.email, 'test@example.com')

    def test_create_user_duplicate_username(self):
    """測試建立使用者時使用者名稱重複"""
    existing_user = Mock()
    self.db.session.query.return_value.filter_by.return_value.first.return_value = existing_user

    with self.assertRaises(ValueError) as context:
    self.user_service.create_user(
    username='existing',
    email='test@example.com',
    password='password123'
    )

    self.assertEqual(str(context.exception), "Username already exists")
    self.db.session.add.assert_not_called()

    def test_create_user_duplicate_email(self):
    """測試建立使用者時郵箱重複"""
    self.db.session.query.return_value.filter_by.side_effect = [
    None,  # username check
    Mock()  # email check
    ]

    with self.assertRaises(ValueError) as context:
    self.user_service.create_user(
    username='testuser',
    email='existing@example.com',
    password='password123'
    )

    self.assertEqual(str(context.exception), "Email already exists")
    self.db.session.add.assert_not_called()

    def test_get_user_success(self):
    """測試成功獲取使用者"""
    user = User(id=1, username='testuser', email='test@example.com')
    self.db.session.query.return_value.filter_by.return_value.first.return_value = user

    result = self.user_service.get_user(1)

    self.assertEqual(result.id, 1)
    self.assertEqual(result.username, 'testuser')

    def test_get_user_not_found(self):
    """測試獲取不存在的使用者"""
    self.db.session.query.return_value.filter_by.return_value.first.return_value = None

    with self.assertRaises(ValueError) as context:
    self.user_service.get_user(999)

    self.assertEqual(str(context.exception), "User not found")

    def test_update_user_username(self):
    """測試更新使用者名稱"""
    user = User(id=1, username='olduser', email='test@example.com')
    self.db.session.query.return_value.filter_by.return_value.first.return_value = user

    updated_user = self.user_service.update_user(1, username='newuser')

    self.assertEqual(updated_user.username, 'newuser')
    self.db.session.commit.assert_called_once()

    def test_update_user_duplicate_username(self):
    """測試更新使用者名稱時重複"""
    user = User(id=1, username='olduser', email='test@example.com')
    existing_user = User(id=2, username='existing', email='other@example.com')

    self.db.session.query.return_value.filter_by.side_effect = [
    user,  # get_user
    existing_user  # username check
    ]

    with self.assertRaises(ValueError) as context:
    self.user_service.update_user(1, username='existing')

    self.assertEqual(str(context.exception), "Username already exists")
    self.db.session.commit.assert_not_called()

    def test_update_user_password(self):
    """測試更新密碼"""
    user = User(id=1, username='testuser', email='test@example.com')
    self.db.session.query.return_value.filter_by.return_value.first.return_value = user

    updated_user = self.user_service.update_user(1, password='newpassword')

    self.assertIsNotNone(updated_user.password_hash)
    self.assertNotEqual(updated_user.password_hash, user.password_hash)
    self.db.session.commit.assert_called_once()

    def test_delete_user_success(self):
    """測試成功刪除使用者"""
    user = User(id=1, username='testuser', email='test@example.com')
    self.db.session.query.return_value.filter_by.return_value.first.return_value = user

    self.user_service.delete_user(1)

    self.db.session.delete.assert_called_once_with(user)
    self.db.session.commit.assert_called_once()

    def test_delete_user_not_found(self):
    """測試刪除不存在的使用者"""
    self.db.session.query.return_value.filter_by.return_value.first.return_value = None

    with self.assertRaises(ValueError) as context:
    self.user_service.delete_user(999)

    self.assertEqual(str(context.exception), "User not found")
    self.db.session.delete.assert_not_called()

    if __name__ == '__main__':
    unittest.main()

    ```> **生成的测试包含**:
    > - 成功场景测试
    > - 失败场景测试
    > - 边界条件测试
    > - Mock 和断言

    ```

    ## 測試覆蓋率分析

    ### 1. 覆蓋率報告

    # 示例:生成測試覆蓋率報告

    使用者請求:
    "分析測試覆蓋率"
    Claude Code 生成的報告:

    ## 測試覆蓋率報告

    ### 總體覆蓋率

    | 指標 | 覆蓋率 | 目標 | 狀態 |
    |------|--------|------|------|
    | 語句覆蓋率 | 87.5% | 80% | ✓ 達標 |
    | 分支覆蓋率 | 78.2% | 75% | ✓ 達標 |
    | 函式覆蓋率 | 92.3% | 90% | ✓ 達標 |
    | 行覆蓋率 | 85.6% | 80% | ✓ 達標 |

    ### 模組覆蓋率

    | 模組 | 語句覆蓋率 | 分支覆蓋率 | 函式覆蓋率 | 狀態 |
    |------|------------|------------|------------|------|
    | user_service.py | 95.2% | 88.5% | 100% | ✓ 優秀 |
    | product_service.py | 82.3% | 72.1% | 85.7% | ✓ 良好 |
    | order_service.py | 78.5% | 65.4% | 80.0% | ⚠ 需改進 |
    | payment_service.py | 90.1% | 85.2% | 93.3% | ✓ 優秀 |
    | auth_service.py | 88.7% | 80.5% | 91.7% | ✓ 良好 |

    ### 未覆蓋的程式碼

    #### order_service.py

    ````python
    `python

    # 第 45-50 行:未覆盖
    def process_order(self, order_id):
    """处理订单"""
    order = self.get_order(order_id)

    if order.status == 'cancelled':
    raise InvalidOrderStatusError("Cannot process cancelled order")

     # ... 未测试的代码

    ```> **建議**: 新增取消訂單的處理測試

    #### order_service.py

    ```
    python

    # 第 78-85 行:未覆盖
    def refund_order(self, order_id, reason):
        """退款订单"""
        order = self.get_order(order_id)

        if order.status not in ['paid', 'shipped']:
            raise InvalidOrderStatusError("Cannot refund order")

        if not order.payment_id:
            raise PaymentNotFoundError("Payment not found")

        # ... 未测试的代码

    > **建议**: 添加退款订单的测试

    ### 覆盖率提升建议

    1. **order_service.py**
     - 添加取消订单处理测试
     - 添加退款订单测试
     - 添加订单状态转换测试

    2. **product_service.py**
     - 添加库存不足测试
     - 添加商品分类测试
     - 添加商品搜索测试

    ### 测试质量评估

    | 指标 | 得分 | 说明 |
    |------|------|------|
    | 测试覆盖率 | 87.5% | 良好 |
    | 测试有效性 | 85.2% | 良好 |
    | 断言质量 | 90.3% | 优秀 |
    | 测试独立性 | 88.7% | 优秀 |
    | 测试可维护性 | 82.5% | 良好 |

    ### 2. 测试盲点识别

    ```

    python

    # 示例:識別測試盲點

    使用者請求:
    "識別測試盲點"

    Claude Code 生成的分析:

    ## 測試盲點分析

    ### 識別的盲點

    #### 1. 邊界條件盲點

    ```python

    ```python

    # 程式碼:order_service.py

    def calculate_discount(self, order):
        """計算折扣"""
        if order.total_amount > 1000:
            discount = 0.1
        elif order.total_amount > 500:
            discount = 0.05
        else:
            discount = 0

        return order.total_amount * (1 - discount)

    > **盲點**:
    > - 未測試邊界值:500, 1000
    > - 未測試負金額
    > - 未測試零金額

    > **建議測試**:

    ````python

    ````python

    def test_discount_boundary_500(self):
    """测试 500 边界"""
    order = Order(total_amount=500)
    discount = self.service.calculate_discount(order)
    self.assertEqual(discount, 500 * 0.95)

    def test_discount_boundary_1000(self):
    """测试 1000 边界"""
    order = Order(total_amount=1000)
    discount = self.service.calculate_discount(order)
    self.assertEqual(discount, 1000 * 0.9)

    def test_discount_zero_amount(self):
    """测试零金额"""
    order = Order(total_amount=0)
    discount = self.service.calculate_discount(order)
    self.assertEqual(discount, 0)

    python

    # 代码:payment_service.py

    def process_payment(self, payment_data):
        """处理支付"""
        try:
            result = self.gateway.charge(payment_data)
            return result
        except PaymentGatewayError as e:
            logger.error(f"Payment failed: {e}")
            raise PaymentProcessingError(str(e))

    > **盲点**:
    > - 未测试网关超时
    > - 未测试网络错误
    > - 未测试无效支付数据

    > **建议测试**:

    def test_payment_gateway_timeout(self):
    """測試閘道器超時"""
    self.gateway.charge.side_effect = TimeoutError()

    with self.assertRaises(PaymentProcessingError):
    self.service.process_payment(self.payment_data)

    def test_payment_network_error(self):
    """測試網路錯誤"""
    self.gateway.charge.side_effect = ConnectionError()

    with self.assertRaises(PaymentProcessingError):
    self.service.process_payment(self.payment_data)

    def test_payment_invalid_data(self):
    """測試無效支付資料"""
    invalid_data = {'invalid': 'data'}

    with self.assertRaises(ValidationError):
    self.service.process_payment(invalid_data)

    ```#### 3. 并发场景盲点

    ```

    python

    # 程式碼:inventory_service.py

    def update_stock(self, product_id, quantity):
        """更新庫存"""
        product = self.get_product(product_id)
        product.stock += quantity
        self.db.session.commit()
        return product

    > **盲點**:
    > - 未測試併發更新
    > - 未測試庫存不足
    > - 未測試負庫存

    > **建議測試**:

    ````python

    ````python

    def test_concurrent_stock_update(self):
    """测试并发库存更新"""
    product = self.create_product(stock=10)

    with ThreadPoolExecutor(max_workers=2) as executor:
    future1 = executor.submit(self.service.update_stock, product.id, 5)
    future2 = executor.submit(self.service.update_stock, product.id, 3)

    result1 = future1.result()
    result2 = future2.result()

    updated_product = self.service.get_product(product.id)
    self.assertEqual(updated_product.stock, 18)

    def test_insufficient_stock(self):
    """测试库存不足"""
    product = self.create_product(stock=5)

    with self.assertRaises(InsufficientStockError):
    self.service.update_stock(product.id, -10)

    python

    # 代码:search_service.py

    def search_products(self, query, limit=100):
        """搜索商品"""
        results = Product.query.filter(
            Product.name.ilike(f'%{query}%')
        ).limit(limit).all()
        return results

    > **盲点**:
    > - 未测试大量结果
    > - 未测试查询性能
    > - 未测试内存使用

    > **建议测试**:

    def test_search_large_result_set(self):
    """測試大量結果"""
    for i in range(1000):
    self.create_product(name=f'product{i}')

    results = self.service.search_products('product', limit=100)

    self.assertEqual(len(results), 100)
    self.assertLess(self.service.get_query_time(), 0.1)

    ```### 盲点优先级

    | 盲点类型 | 优先级 | 风险等级 |
    |----------|--------|----------|
    | 边界条件 |||
    | 异常处理 |||
    | 并发场景 |||
    | 性能边界 |||

    ```

    ## 測試最佳化建議

    ### 1. 測試效能最佳化

    # 示例:最佳化測試效能

    問題:測試執行時間過長
    最佳化前:

    ````python
    `python

    def test_user_crud_operations(self):
    """测试用户 CRUD 操作"""
    for i in range(100):
    user = self.service.create_user(f'user{i}', f'user{i}@example.com', 'password')
    retrieved_user = self.service.get_user(user.id)
    self.assertEqual(retrieved_user.username, f'user{i}')
    self.service.delete_user(user.id)

    ```> 最佳化後:

    ```
    python

    def test_user_crud_operations(self):
        """测试用户 CRUD 操作"""
        users = []

        for i in range(10):
            user = self.service.create_user(f'user{i}', f'user{i}@example.com', 'password')
            users.append(user)

        for user in users:
            retrieved_user = self.service.get_user(user.id)
            self.assertEqual(retrieved_user.username, user.username)

        for user in users:
            self.service.delete_user(user.id)

    > **效果**: 测试时间从 50 秒降低到 5

    ### 2. 测试可维护性优化

    ```

    python

    # 示例:提高測試可維護性

    問題:測試程式碼重複

    最佳化前:

    ```python

    ```python

    def test_create_user_with_valid_data(self):
        """測試建立使用者"""
        user = self.service.create_user('testuser', 'test@example.com', 'password123')
        self.assertEqual(user.username, 'testuser')
        self.assertEqual(user.email, 'test@example.com')
        self.assertIsNotNone(user.password_hash)

    def test_create_user_with_another_valid_data(self):
        """測試建立另一個使用者"""
        user = self.service.create_user('another', 'another@example.com', 'password456')
        self.assertEqual(user.username, 'another')
        self.assertEqual(user.email, 'another@example.com')
        self.assertIsNotNone(user.password_hash)

    最佳化後:

    ````python

    ````python

    def _create_user_data(self, username='testuser', email='test@example.com', password='password123'):
    """创建用户数据辅助方法"""
    return {
    'username': username,
    'email': email,
    'password': password
    }

    def _assert_user_created(self, user, expected_data):
    """断言用户创建成功辅助方法"""
    self.assertEqual(user.username, expected_data['username'])
    self.assertEqual(user.email, expected_data['email'])
    self.assertIsNotNone(user.password_hash)

    def test_create_user_with_valid_data(self):
    """测试创建用户"""
    data = self._create_user_data()
    user = self.service.create_user(**data)
    self._assert_user_created(user, data)

    def test_create_user_with_another_valid_data(self):
    """测试创建另一个用户"""
    data = self._create_user_data(username='another', email='another@example.com')
    user = self.service.create_user(**data)
    self._assert_user_created(user, data)

    ## 总结

    智能测试包括:

    1. **自动生成测试**: 单元测试生成、集成测试生成
    2. **测试覆盖率分析**: 覆盖率报告、测试盲点识别
    3. **测试优化建议**: 性能优化、可维护性优化

    通过这些功能,开发者可以快速构建全面的测试套件,提高代码质量和可靠性。

    在下一节中,我们将探讨智能部署。

基于 MIT 许可发布 | 永久导航