Qwen2.5-32B-Instruct单元测试指南:pytest实战教程

写代码最怕什么?不是写不出来,而是写出来的代码跑着跑着就崩了,你还不知道问题出在哪。单元测试就像是给代码买的保险,出了问题能第一时间知道哪里坏了,怎么修。但说实话,写测试这事儿挺烦人的,特别是当你面对一堆复杂的业务逻辑时,光是设计测试用例就够头疼的。

最近我在用Qwen2.5-32B-Instruct这个模型,发现它在代码生成方面确实有两把刷子。我就想,能不能让它帮我写单元测试呢?试了几次之后,效果还真不错。今天我就来分享一下,怎么用这个模型配合pytest,让写单元测试这件事变得轻松一些。

1. 环境准备与快速上手

1.1 安装必要的工具

首先,你得有个能跑Qwen2.5-32B-Instruct的环境。这个模型对硬件要求不低,32B的参数量,建议至少准备24GB以上的显存。如果你用的是云端服务或者本地有足够的资源,可以按下面的步骤来。

# 安装transformers库,建议用最新版本
pip install transformers torch accelerate

# 安装pytest,这是我们今天的主角
pip install pytest pytest-cov pytest-mock

# 如果你需要生成测试报告,可以再装个pytest-html
pip install pytest-html

1.2 快速加载模型

加载Qwen2.5-32B-Instruct其实挺简单的,几行代码就能搞定。不过要注意,这个模型比较大,加载需要一些时间,也占不少内存。

from transformers import AutoModelForCausalLM, AutoTokenizer

# 指定模型名称
model_name = "Qwen/Qwen2.5-32B-Instruct"

# 加载模型和分词器
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype="auto",  # 自动选择数据类型
    device_map="auto"    # 自动分配设备
)
tokenizer = AutoTokenizer.from_pretrained(model_name)

print("模型加载完成,可以开始使用了!")

如果你显存不够,可以考虑用量化版本,或者用CPU模式(虽然会慢很多)。不过对于写测试代码这种任务,其实对速度要求没那么高,能跑起来就行。

2. 让AI帮你设计测试用例

写测试最难的部分是什么?我觉得是设计测试用例。你要考虑各种边界情况、异常场景,还得保证覆盖所有重要的代码路径。这事儿让AI来做,反而挺合适的。

2.1 基础测试用例生成

假设你写了一个简单的函数,想为它生成测试用例。先看看这个函数:

# 这是我们要测试的函数
def calculate_discount(price, is_member, coupon_code=None):
    """
    计算商品折扣价格
    - price: 原价
    - is_member: 是否是会员
    - coupon_code: 优惠券代码,可选
    """
    if price <= 0:
        raise ValueError("价格必须大于0")
    
    discount = 0
    
    # 会员折扣
    if is_member:
        discount += 0.1  # 会员打9折
    
    # 优惠券折扣
    if coupon_code == "SAVE10":
        discount += 0.1
    elif coupon_code == "SAVE20":
        discount += 0.2
    
    # 折扣不能超过50%
    discount = min(discount, 0.5)
    
    final_price = price * (1 - discount)
    return round(final_price, 2)

现在,我们让Qwen2.5-32B-Instruct帮我们生成测试用例:

def generate_test_cases(function_code, function_description):
    """
    使用Qwen2.5生成测试用例
    """
    prompt = f"""
请为以下Python函数编写pytest测试用例。函数描述:{function_description}

函数代码:
```python
{function_code}

要求:

  1. 覆盖所有正常情况
  2. 覆盖所有边界情况
  3. 覆盖所有异常情况
  4. 每个测试用例都要有清晰的名称
  5. 使用pytest的assert语句
  6. 如果有异常测试,使用pytest.raises

请直接输出测试代码,不要解释。 """

messages = [
    {"role": "system", "content": "你是一个专业的Python测试工程师,擅长编写高质量的单元测试。"},
    {"role": "user", "content": prompt}
]

text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)

model_inputs = tokenizer([text], return_tensors="pt").to(model.device)

generated_ids = model.generate(
    **model_inputs,
    max_new_tokens=1000,
    temperature=0.3,  # 温度设低一点,让输出更确定
    do_sample=True
)

generated_ids = [
    output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
]

response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
return response

使用示例

test_code = generate_test_cases(calculate_discount.doc, inspect.getsource(calculate_discount)) print("生成的测试代码:") print(test_code)


运行这个代码,AI会给你生成一套完整的测试用例。我试了几次,生成的测试代码质量还不错,基本能覆盖各种情况。

### 2.2 处理复杂的测试场景

有时候我们的函数更复杂,比如涉及数据库操作、网络请求或者文件读写。这时候写测试就更麻烦了,因为你要模拟这些外部依赖。不过别担心,AI也能帮忙。

假设我们有一个用户服务类:

```python
class UserService:
    def __init__(self, db_connection):
        self.db = db_connection
    
    def register_user(self, username, email, password):
        """注册新用户"""
        if not username or not email or not password:
            raise ValueError("所有字段都不能为空")
        
        if len(password) < 8:
            raise ValueError("密码长度至少8位")
        
        # 检查用户名是否已存在
        existing_user = self.db.query("SELECT * FROM users WHERE username = ?", (username,))
        if existing_user:
            raise ValueError("用户名已存在")
        
        # 插入新用户
        user_id = self.db.execute(
            "INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)",
            (username, email, self._hash_password(password))
        )
        
        return user_id
    
    def _hash_password(self, password):
        """密码哈希函数(简化版)"""
        import hashlib
        return hashlib.sha256(password.encode()).hexdigest()

对于这种有外部依赖的代码,我们需要用Mock来模拟数据库。让AI生成这种测试:

def generate_mock_tests(class_code, class_description):
    """
    生成包含Mock的测试用例
    """
    prompt = f"""
请为以下Python类编写pytest测试用例,需要使用pytest-mock来模拟外部依赖。

类描述:{class_description}

类代码:
```python
{class_code}

要求:

  1. 使用@pytest.fixture设置测试固件
  2. 使用mocker.patch来模拟数据库连接
  3. 测试所有正常情况
  4. 测试所有异常情况
  5. 验证Mock对象的调用情况
  6. 每个测试用例都要有清晰的名称

请直接输出测试代码,不要解释。 """

# 同样的生成逻辑...
return response

AI生成的测试代码会包含Mock对象,这样我们就能在不连接真实数据库的情况下测试业务逻辑了。

## 3. 测试覆盖率分析与优化

生成了测试用例还不够,我们还得知道测试覆盖了哪些代码,哪些没覆盖。pytest-cov这个工具能帮我们分析测试覆盖率。

### 3.1 运行测试并查看覆盖率

先写个简单的测试文件,然后运行覆盖率分析:

```bash
# 运行测试并生成覆盖率报告
pytest --cov=your_module --cov-report=html --cov-report=term-missing tests/

# 如果你只想看总体覆盖率
pytest --cov=your_module tests/

运行后会显示类似这样的输出:

----------- coverage: platform linux, python 3.9.0 -----------
Name                    Stmts   Miss  Cover   Missing
-----------------------------------------------------
your_module/__init__.py      0      0   100%
your_module/service.py      45      8    82%   24-27, 35-38, 42-43
-----------------------------------------------------
TOTAL                      45      8    82%

3.2 让AI帮我们提高覆盖率

看到哪些行没被覆盖后,我们可以让AI专门为这些代码生成测试用例:

def generate_coverage_tests(function_code, uncovered_lines, function_description):
    """
    为未覆盖的代码行生成测试用例
    """
    prompt = f"""
以下Python函数有一些代码行没有被测试覆盖:
未覆盖的行:{uncovered_lines}

函数描述:{function_description}

函数代码:
```python
{function_code}

请专门为这些未覆盖的代码行编写pytest测试用例,确保这些行能被执行到。

要求:

  1. 针对性地设计测试用例
  2. 确保能触发未覆盖的代码路径
  3. 测试用例名称要说明测试的是什么
  4. 使用pytest的assert语句

请直接输出测试代码,不要解释。 """

# 生成测试代码...
return response

这种方法特别有用,因为有时候我们写的测试可能漏掉了一些边界情况或者错误处理分支。AI能帮我们发现这些盲点。

## 4. 实用的测试技巧与最佳实践

### 4.1 参数化测试

pytest有个很好用的功能叫参数化测试,可以用一组数据测试同一个函数。AI也能帮我们生成这种测试:

```python
def generate_parametrized_tests(function_code, test_cases, function_description):
    """
    生成参数化测试
    """
    prompt = f"""
请使用pytest的@pytest.mark.parametrize装饰器为以下函数编写测试。

函数描述:{function_description}

函数代码:
```python
{function_code}

测试用例数据: {test_cases}

要求:

  1. 使用参数化测试
  2. 为每组数据提供预期的输出
  3. 测试用例名称要清晰
  4. 包含边界情况和异常情况

请直接输出测试代码,不要解释。 """

return response

示例测试用例数据

test_cases = """ 测试用例:

  1. 输入:price=100, is_member=True, coupon_code="SAVE10" → 预期输出:81.0
  2. 输入:price=200, is_member=False, coupon_code=None → 预期输出:200.0
  3. 输入:price=50, is_member=True, coupon_code="SAVE20" → 预期输出:35.0
  4. 输入:price=0, is_member=False, coupon_code=None → 预期抛出ValueError
  5. 输入:price=-10, is_member=True, coupon_code="SAVE10" → 预期抛出ValueError """

### 4.2 测试夹具(Fixtures)的使用

对于需要共享的测试资源,比如数据库连接、临时文件等,可以用pytest的fixture。AI也能帮我们设计合理的fixture:

```python
def generate_test_with_fixtures(class_code, class_description):
    """
    生成包含fixture的测试
    """
    prompt = f"""
请为以下Python类编写pytest测试用例,需要使用@pytest.fixture来管理测试资源。

类描述:{class_description}

类代码:
```python
{class_code}

要求:

  1. 创建适当的fixture(如临时数据库、测试数据等)
  2. 在测试中使用这些fixture
  3. 测试所有公共方法
  4. 包含清理资源的逻辑
  5. 使用pytest-mock模拟外部依赖

请直接输出测试代码,不要解释。 """

return response

### 4.3 集成测试与端到端测试

除了单元测试,有时候我们还需要集成测试。AI也能帮我们设计更复杂的测试场景:

```python
def generate_integration_tests(module_code, module_description):
    """
    生成集成测试用例
    """
    prompt = f"""
请为以下模块编写集成测试用例,测试多个组件之间的交互。

模块描述:{module_description}

模块代码:
```python
{module_code}

要求:

  1. 测试完整的业务流程
  2. 模拟真实的使用场景
  3. 验证组件间的数据流
  4. 包含错误处理和恢复测试
  5. 使用适当的测试数据

请直接输出测试代码,不要解释。 """

return response

## 5. 实际应用示例

让我用一个完整的例子来展示整个过程。假设我们有一个电商系统的折扣计算模块:

```python
# discount_calculator.py
class DiscountCalculator:
    def __init__(self, user_service, promotion_service):
        self.user_service = user_service
        self.promotion_service = promotion_service
    
    def calculate_final_price(self, user_id, product_id, quantity):
        """计算最终价格"""
        # 获取用户信息
        user = self.user_service.get_user(user_id)
        if not user:
            raise ValueError("用户不存在")
        
        # 获取商品信息
        product = self.promotion_service.get_product(product_id)
        if not product:
            raise ValueError("商品不存在")
        
        # 基础价格
        base_price = product.price * quantity
        
        # 用户等级折扣
        user_discount = self._get_user_discount(user.level)
        
        # 促销活动折扣
        promotion_discount = self.promotion_service.get_current_promotion(product_id)
        
        # 总折扣(不能超过80%)
        total_discount = min(user_discount + promotion_discount, 0.8)
        
        # 最终价格
        final_price = base_price * (1 - total_discount)
        
        # 四舍五入到两位小数
        return round(final_price, 2)
    
    def _get_user_discount(self, user_level):
        """根据用户等级获取折扣"""
        discounts = {
            "bronze": 0.05,
            "silver": 0.1,
            "gold": 0.15,
            "platinum": 0.2
        }
        return discounts.get(user_level, 0)

现在,我们让AI为这个类生成完整的测试套件:

# 生成测试代码
test_code = generate_test_cases(
    inspect.getsource(DiscountCalculator),
    "电商折扣计算器,根据用户等级和促销活动计算最终价格"
)

# 保存到文件
with open("test_discount_calculator.py", "w", encoding="utf-8") as f:
    f.write(test_code)

print("测试文件已生成:test_discount_calculator.py")

生成的测试代码大概长这样:

# test_discount_calculator.py
import pytest
from unittest.mock import Mock, MagicMock
from discount_calculator import DiscountCalculator

class TestDiscountCalculator:
    @pytest.fixture
    def mock_user_service(self):
        """模拟用户服务"""
        service = Mock()
        return service
    
    @pytest.fixture
    def mock_promotion_service(self):
        """模拟促销服务"""
        service = Mock()
        return service
    
    @pytest.fixture
    def calculator(self, mock_user_service, mock_promotion_service):
        """创建折扣计算器实例"""
        return DiscountCalculator(mock_user_service, mock_promotion_service)
    
    def test_calculate_final_price_normal_case(self, calculator, mock_user_service, mock_promotion_service):
        """测试正常情况下的价格计算"""
        # 设置Mock返回值
        mock_user_service.get_user.return_value = {"id": 1, "level": "gold"}
        mock_promotion_service.get_product.return_value = {"id": 101, "price": 100.0}
        mock_promotion_service.get_current_promotion.return_value = 0.1
        
        # 执行计算
        result = calculator.calculate_final_price(1, 101, 2)
        
        # 验证结果
        # 基础价格:100 * 2 = 200
        # 用户折扣:gold等级15%
        # 促销折扣:10%
        # 总折扣:15% + 10% = 25%
        # 最终价格:200 * (1 - 0.25) = 150.0
        assert result == 150.0
        
        # 验证Mock调用
        mock_user_service.get_user.assert_called_once_with(1)
        mock_promotion_service.get_product.assert_called_once_with(101)
        mock_promotion_service.get_current_promotion.assert_called_once_with(101)
    
    def test_calculate_final_price_user_not_found(self, calculator, mock_user_service, mock_promotion_service):
        """测试用户不存在的情况"""
        mock_user_service.get_user.return_value = None
        
        with pytest.raises(ValueError, match="用户不存在"):
            calculator.calculate_final_price(999, 101, 1)
    
    def test_calculate_final_price_product_not_found(self, calculator, mock_user_service, mock_promotion_service):
        """测试商品不存在的情况"""
        mock_user_service.get_user.return_value = {"id": 1, "level": "silver"}
        mock_promotion_service.get_product.return_value = None
        
        with pytest.raises(ValueError, match="商品不存在"):
            calculator.calculate_final_price(1, 999, 1)
    
    def test_calculate_final_price_max_discount(self, calculator, mock_user_service, mock_promotion_service):
        """测试折扣上限(不超过80%)"""
        mock_user_service.get_user.return_value = {"id": 1, "level": "platinum"}  # 20%折扣
        mock_promotion_service.get_product.return_value = {"id": 101, "price": 100.0}
        mock_promotion_service.get_current_promotion.return_value = 0.7  # 70%促销折扣
        
        result = calculator.calculate_final_price(1, 101, 1)
        
        # 总折扣应该是20% + 70% = 90%,但上限是80%
        # 所以最终价格:100 * (1 - 0.8) = 20.0
        assert result == 20.0
    
    @pytest.mark.parametrize("user_level,expected_discount", [
        ("bronze", 0.05),
        ("silver", 0.1),
        ("gold", 0.15),
        ("platinum", 0.2),
        ("unknown", 0),  # 未知等级无折扣
        ("", 0),  # 空字符串
        (None, 0),  # None值
    ])
    def test_get_user_discount(self, calculator, user_level, expected_discount):
        """测试用户等级折扣计算"""
        result = calculator._get_user_discount(user_level)
        assert result == expected_discount
    
    def test_calculate_final_price_edge_cases(self, calculator, mock_user_service, mock_promotion_service):
        """测试边界情况"""
        test_cases = [
            # (用户等级, 商品价格, 数量, 促销折扣, 预期结果)
            ("bronze", 99.99, 10, 0, 949.90),  # 只有基础会员折扣
            ("platinum", 0.01, 100, 0.5, 1.0),  # 极低单价,高折扣
            ("gold", 1000, 0, 0.1, 0.0),  # 数量为0
        ]
        
        for user_level, price, quantity, promotion_discount, expected in test_cases:
            mock_user_service.get_user.return_value = {"id": 1, "level": user_level}
            mock_promotion_service.get_product.return_value = {"id": 101, "price": price}
            mock_promotion_service.get_current_promotion.return_value = promotion_discount
            
            result = calculator.calculate_final_price(1, 101, quantity)
            assert result == expected, f"测试失败:user_level={user_level}, price={price}, quantity={quantity}"

6. 常见问题与解决方案

在实际使用中,你可能会遇到一些问题。这里我总结了一些常见的情况和解决办法:

6.1 AI生成的测试不完整怎么办?

有时候AI可能漏掉一些重要的测试场景。这时候你可以:

  1. 提供更详细的提示:在prompt中明确要求覆盖哪些特定场景
  2. 分步生成:先让AI生成基础测试,再让它补充边界情况测试
  3. 人工审查和补充:AI是辅助工具,最终还需要人工检查
def generate_specific_tests(function_code, specific_scenarios, function_description):
    """
    为特定场景生成测试
    """
    prompt = f"""
请为以下函数编写测试用例,特别关注这些场景:
{specific_scenarios}

函数描述:{function_description}

函数代码:
```python
{function_code}

请为每个场景编写一个测试用例。 """

return response

### 6.2 测试代码质量不高怎么办?

如果AI生成的测试代码质量不高,可以尝试:

1. **调整温度参数**:把temperature调低(比如0.2),让输出更稳定
2. **提供示例**:在prompt中给出好的测试代码示例
3. **迭代优化**:先生成基础版本,然后让AI基于你的反馈改进

```python
def improve_test_code(original_test_code, feedback):
    """
    改进测试代码
    """
    prompt = f"""
以下是现有的测试代码,但有一些问题:
{feedback}

现有测试代码:
```python
{original_test_code}

请根据反馈改进测试代码,解决提到的问题。 """

return response

### 6.3 如何处理复杂的测试数据?

对于需要复杂测试数据的情况,可以让AI生成测试数据生成器:

```python
def generate_test_data_generator(data_structure, requirements):
    """
    生成测试数据生成器
    """
    prompt = f"""
请编写一个测试数据生成器,用于生成符合以下要求的数据:

数据结构:
{data_structure}

要求:
{requirements}

请输出完整的Python代码,包含生成测试数据的函数。
"""
    
    return response

7. 总结

用Qwen2.5-32B-Instruct来辅助写单元测试,确实能省不少事。特别是对于那些重复性高、模式固定的测试代码,AI能快速生成不错的初稿。不过要记住,AI生成的东西终究需要人工审查和调整,不能完全依赖。

我自己的体会是,最好的工作流程是:先让AI生成测试框架和基础用例,然后人工检查补充,特别是那些业务逻辑复杂、容易出错的地方。AI擅长处理模式化的任务,而人类更擅长理解业务上下文和潜在的风险点。

另外,测试覆盖率工具和AI可以很好地配合使用。先用覆盖率分析找出测试的盲点,再让AI针对这些盲点生成测试用例,这样效率最高。

最后说一句,工具再好也只是工具。写测试的核心目的是保证代码质量,这个目标不会变。AI能帮我们更快地达到这个目标,但最终的质量责任还是在开发者自己身上。多花点时间理解业务,设计好的测试用例,比单纯追求测试覆盖率数字要有意义得多。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐