news 2026/5/11 9:28:32

Python单元测试与测试驱动开发:从入门到实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python单元测试与测试驱动开发:从入门到实践

Python单元测试与测试驱动开发:从入门到实践

引言

测试驱动开发(TDD)是一种软件开发方法论,强调在编写实际代码之前先编写测试。Python提供了强大的测试框架支持,使得TDD成为一种高效的开发方式。

本文将深入探讨Python单元测试的核心概念,并分享TDD的最佳实践。

一、单元测试基础

1.1 使用unittest模块

import unittest class TestMathOperations(unittest.TestCase): def setUp(self): """测试前置条件""" self.base = 10 def tearDown(self): """测试清理工作""" pass def test_addition(self): """测试加法运算""" result = self.base + 5 self.assertEqual(result, 15) def test_subtraction(self): """测试减法运算""" result = self.base - 3 self.assertTrue(result == 7) def test_multiplication(self): """测试乘法运算""" result = self.base * 2 self.assertNotEqual(result, 19) if __name__ == '__main__': unittest.main()

1.2 使用pytest框架

import pytest def add(a, b): return a + b def test_add_positive_numbers(): assert add(2, 3) == 5 def test_add_negative_numbers(): assert add(-1, -1) == -2 def test_add_with_zero(): assert add(0, 0) == 0 @pytest.mark.parametrize("a, b, expected", [ (1, 2, 3), (-1, 1, 0), (0, 0, 0), (10, 20, 30), ]) def test_add_multiple_cases(a, b, expected): assert add(a, b) == expected

二、测试驱动开发流程

2.1 TDD三步法

# 步骤1: 编写失败的测试 def test_calculate_discount(): """测试折扣计算""" price = 100 discount = 0.2 expected = 80 assert calculate_discount(price, discount) == expected # 步骤2: 编写最小代码使测试通过 def calculate_discount(price, discount): return price * (1 - discount) # 步骤3: 重构代码 def calculate_discount(price: float, discount: float) -> float: """计算折扣后价格""" if discount < 0 or discount > 1: raise ValueError("折扣必须在0到1之间") return price * (1 - discount)

2.2 TDD实战示例

# 需求:实现一个栈数据结构 class TestStack: def test_stack_is_empty_initially(self): stack = Stack() assert stack.is_empty() def test_push_adds_element(self): stack = Stack() stack.push(1) assert not stack.is_empty() def test_pop_returns_last_element(self): stack = Stack() stack.push(1) stack.push(2) assert stack.pop() == 2 def test_pop_from_empty_stack_raises_error(self): stack = Stack() with pytest.raises(IndexError): stack.pop() # 实现栈 class Stack: def __init__(self): self.items = [] def is_empty(self): return len(self.items) == 0 def push(self, item): self.items.append(item) def pop(self): if self.is_empty(): raise IndexError("Cannot pop from empty stack") return self.items.pop()

三、高级测试技术

3.1 Mock对象

from unittest.mock import Mock, patch def test_api_call(mocker): # 创建mock对象 mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = {"data": "test"} # 替换requests.get with patch('requests.get', return_value=mock_response): result = fetch_data('https://api.example.com') assert result == {"data": "test"} requests.get.assert_called_once_with('https://api.example.com')

3.2 Fixture机制

import pytest @pytest.fixture def database_connection(): """创建数据库连接fixture""" conn = create_connection() yield conn conn.close() @pytest.fixture def test_user(): """创建测试用户fixture""" user = User(name="Test", email="test@example.com") return user def test_user_creation(database_connection, test_user): """测试用户创建""" database_connection.save(test_user) retrieved = database_connection.get_by_id(test_user.id) assert retrieved.name == "Test"

3.3 参数化测试

import pytest @pytest.mark.parametrize( "input_data, expected", [ ("hello", "HELLO"), ("world", "WORLD"), ("Python", "PYTHON"), ], ids=["lowercase", "mixed_case", "title_case"] ) def test_string_upper(input_data, expected): assert input_data.upper() == expected @pytest.mark.parametrize("value", [1, 2, 3, 4, 5]) def test_positive_numbers(value): assert value > 0

四、测试覆盖率

4.1 使用coverage.py

# 安装coverage pip install coverage # 运行测试并生成报告 coverage run -m pytest tests/ coverage report -m coverage html

4.2 覆盖率目标

# .coveragerc配置文件 [run] source = . omit = */tests/* */__init__.py [report] show_missing = True fail_under = 80

五、测试最佳实践

5.1 测试命名规范

# 好的测试命名 def test_user_can_login_with_valid_credentials(): pass def test_user_cannot_login_with_invalid_password(): pass def test_api_returns_404_for_nonexistent_resource(): pass # 避免的命名 def test_login(): # 太模糊 pass def test_case_1(): # 没有描述性 pass

5.2 测试隔离

def test_database_transactions(): """每个测试应该独立""" # 在测试开始时清理状态 clear_database() # 执行测试 create_user("test") # 验证结果 assert get_user_count() == 1 def clear_database(): """清理数据库""" # 删除所有数据 pass

5.3 测试文档化

def test_checkout_process_with_insufficient_stock(): """ 测试库存不足时的结账流程 场景:用户尝试购买库存不足的商品 预期:系统应返回错误信息,订单不应创建 """ product = Product(name="Book", stock=0) cart = Cart(items=[product]) with pytest.raises(InsufficientStockError): checkout(cart)

六、持续集成中的测试

6.1 GitHub Actions配置

# .github/workflows/tests.yml name: Run Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip pip install pytest pytest-mock coverage pip install -e . - name: Run tests run: pytest tests/ -v - name: Check coverage run: coverage run -m pytest tests/ && coverage report --fail-under=80

6.2 测试报告集成

# 生成JUnit格式报告 pytest tests/ --junitxml=results.xml # 发送测试结果到Slack def notify_slack(results): message = f"测试完成: {results.passed}/{results.total} 通过" send_slack_message(message)

七、总结

TDD的优势:

  1. 更早发现问题:在开发早期捕获bug
  2. 更好的设计:测试驱动产生更清晰的API
  3. 文档作用:测试用例作为活文档
  4. 重构信心:测试确保重构不会破坏功能

在实际项目中,建议:

  • 采用TDD方法论
  • 保持测试简洁独立
  • 追求合理的测试覆盖率
  • 集成到CI/CD流程中

思考:在你的项目中,TDD带来了哪些好处?欢迎分享!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 9:24:44

宝塔面板登录教程

1买个服务器2连接ssh-宝塔或者xshell都行3在xshell下载宝塔面板4在服务器主页--在哪里订购的就在有个管理点进去-加入安全组或者添加nat转发。如果不行用bt命令重置端口号再访问&#xff0c;最后重置之后重启一下-bt 15使用nat转发的要用外网端口&#xff0c;宝塔显示的是内网的…

作者头像 李华
网站建设 2026/5/11 9:22:40

Windows Defender彻底移除指南:2025年完整解决方案

Windows Defender彻底移除指南&#xff1a;2025年完整解决方案 【免费下载链接】windows-defender-remover A tool which is uses to remove Windows Defender in Windows 8.x, Windows 10 (every version) and Windows 11. 项目地址: https://gitcode.com/gh_mirrors/wi/win…

作者头像 李华
网站建设 2026/5/11 9:22:22

Python习题集:程序11

程序11题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f;代码&#xff1a;def rabbit_total_…

作者头像 李华
网站建设 2026/5/11 9:22:20

2026亲测:商业模式口碑避坑复盘

在商业咨询领域&#xff0c;模式创新与风险规避始终是企业的核心痛点。2025-2026年&#xff0c;大量企业在转型过程中陷入同质化竞争、融资困难、成本高企等困境。调研显示&#xff0c;超过60%的中小企业因“商业模式模糊”导致增长停滞&#xff0c;约45%的企业因“缺乏差异化定…

作者头像 李华
网站建设 2026/5/11 9:18:34

BetterNCM安装器:3分钟让网易云音乐焕然一新

BetterNCM安装器&#xff1a;3分钟让网易云音乐焕然一新 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 还在为网易云音乐PC版功能单一而烦恼吗&#xff1f;想要个性化界面却苦于无从下…

作者头像 李华