首页
学习
活动
专区
圈层
工具
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Python单元测试实战 - Pytest入门到进阶

在软件开发中,测试是保证代码质量的重要环节。作为Python开发者,掌握Pytest这个强大的测试框架将帮助你编写更可靠的代码。本文将深入浅出地介绍Pytest的使用方法,帮助你快速掌握单元测试技能。

为什么选择Pytest?

相比于Python自带的unittest模块,Pytest具有以下优势:

简洁的测试语法,无需创建测试类

强大的断言机制,无需记忆繁琐的断言方法

详细的失败报告

丰富的插件生态系统

支持参数化测试

灵活的夹具(fixture)系统

环境搭建

首先安装Pytest:

pip install pytest

为了获得更好的测试体验,建议同时安装以下常用插件:

pip install pytest-cov  # 用于测试覆盖率统计

pip install pytest-html # 用于生成HTML测试报告

编写第一个测试

让我们从一个简单的例子开始。假设我们有一个计算器模块:

# calculator.py

def add(a, b):

  return a + b

def subtract(a, b):

  return a - b

def multiply(a, b):

  return a * b

def divide(a, b):

  if b == 0:

      raise ValueError("除数不能为0")

  return a / b

现在编写对应的测试文件:

# test_calculator.py

import pytest

from calculator import add, subtract, multiply, divide

def test_add():

  assert add(3, 4) == 7

  assert add(-1, 1) == 0

  assert add(0, 0) == 0

def test_subtract():

  assert subtract(5, 3) == 2

  assert subtract(1, 5) == -4

  assert subtract(0, 0) == 0

def test_multiply():

  assert multiply(3, 4) == 12

  assert multiply(-2, 3) == -6

  assert multiply(0, 5) == 0

def test_divide():

  assert divide(6, 2) == 3

  assert divide(5, 2) == 2.5

  assert divide(-6, 2) == -3

def test_divide_by_zero():

  with pytest.raises(ValueError):

      divide(5, 0)

运行测试:

pytest test_calculator.py -v

Pytest核心特性

1. 断言机制

Pytest的断言非常直观,你可以直接使用Python的assert语句:

def test_string():

  s = "hello"

  assert s.startswith("he")

  assert "e" in s

  assert len(s) == 5

def test_list():

  lst = [1, 2, 3]

  assert 1 in lst

  assert lst[0] == 1

  assert len(lst) == 3

2. 夹具(Fixture)

夹具是Pytest的一大特色,用于提供测试所需的数据或资源:

import pytest

@pytest.fixture

def sample_data():

  return {

      'name': 'Alice',

      'age': 25,

      'city': 'Beijing'

  }

def test_name(sample_data):

  assert sample_data['name'] == 'Alice'

def test_age(sample_data):

  assert sample_data['age'] == 25

3. 参数化测试

使用参数化测试可以用不同的输入值运行同一个测试:

import pytest

@pytest.mark.parametrize("input,expected", [

  ("hello", 5),

  ("python", 6),

  ("test", 4),

  ("", 0),

])

def test_string_length(input, expected):

  assert len(input) == expected

4. 跳过测试和预期失败

有时我们需要跳过某些测试或标记预期失败的测试:

@pytest.mark.skip(reason="功能尚未实现")

def test_future_feature():

  pass

@pytest.mark.xfail

def test_known_bug():

  assert 1 + 1 == 3  # 这个测试会失败,但不会影响测试结果

高级特性

1. 测试覆盖率统计

使用pytest-cov插件统计测试覆盖率:

pytest --cov=myproject tests/

生成HTML格式的覆盖率报告:

pytest --cov=myproject --cov-report=html tests/

2. 配置文件

在项目根目录创建pytest.ini或conftest.py文件进行配置:

[pytest]

testpaths = tests

python_files = test_*.py

python_classes = Test*

python_functions = test_*

addopts = -v --cov=myproject

markers =

  slow: marks tests as slow

  integration: marks tests as integration tests

3. 共享夹具

在conftest.py中定义的夹具可以被多个测试文件共享:

# conftest.py

import pytest

import pymongo

@pytest.fixture(scope="session")

def db_connection():

  client = pymongo.MongoClient("mongodb://localhost:27017/")

  db = client.test_database

  yield db

  client.close()

测试最佳实践

1. 目录结构

推荐的项目结构:

my_project/

├── myproject/

│   ├── __init__.py

│   └── calculator.py

├── tests/

│   ├── __init__.py

│   ├── conftest.py

│   └── test_calculator.py

├── pytest.ini

└── requirements.txt

2. 命名规范

测试文件以test_开头

测试函数以test_开头

测试类以Test开头

夹具函数使用描述性名称

3. 编写原则

「单一职责」:每个测试函数只测试一个功能点

「独立性」:测试之间不应相互依赖

「可读性」:使用清晰的命名和注释

「完整性」:测试各种边界条件和异常情况

「简洁性」:避免冗余的测试代码

常见问题解决

1. 测试执行很慢

使用pytest-xdist插件实现并行测试

标记慢速测试并单独执行

优化夹具的作用域

@pytest.mark.slow

def test_slow_operation():

  pass

# 运行时排除慢速测试

pytest -v -m "not slow"

2. 测试数据管理

使用夹具提供测试数据

考虑使用工厂模式生成测试数据

适当使用模拟(Mock)对象

from unittest.mock import Mock, patch

def test_api_call():

  with patch('requests.get') as mock_get:

      mock_get.return_value.status_code = 200

      mock_get.return_value.json.return_value = {'data': 'test'}

      # 执行测试

3. 配置问题

检查pytest.ini配置

确保Python路径正确

验证插件安装状态

进阶技巧

1. 自定义标记

# pytest.ini

[pytest]

markers =

  integration: 集成测试

  slow: 耗时较长的测试

# 使用自定义标记

@pytest.mark.integration

def test_database_integration():

  pass

2. 钩子函数

在conftest.py中定义钩子函数:

def pytest_runtest_setup(item):

  # 测试开始前的处理

  pass

def pytest_runtest_teardown(item):

  # 测试结束后的处理

  pass

3. 参数化夹具

持续集成整合

将Pytest集成到CI/CD流程中:

Pytest是一个功能强大且易于使用的测试框架,它能帮助你编写高质量的测试代码。通过本文的学习,你应该已经掌握了Pytest的基本用法和一些进阶技巧。记住,好的测试不仅能保证代码质量,还能帮助你更好地理解和设计代码。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OOdrwm3-6Mposp2IXQOv6dDQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券