当前位置: 首页 > news >正文

Python 单元测试与 Mock 体系全解

Python 单元测试与 Mock 体系全解:从 unittest 到 pytest 的主流实践

1. 为什么要写测试?

作为 Java 开发者,你一定熟悉 JUnit + Mockito 这对黄金组合。在 Python 世界里,也有一套完整且成熟的测试生态。区别在于:Python 的测试工具更灵活、更"Pythonic",上手成本低但功能强大。

本文将从 Python 标准库的unittest出发,逐步介绍unittest.mockpytestpytest-mock等主流方案,并穿插 Java 对比帮助你快速建立知识映射。


2. 测试体系全景图

Python 工具定位Java 对应
unittest标准库测试框架JUnit
unittest.mock标准库 Mock 工具Mockito
pytest第三方测试框架(业界主流)JUnit 5 + AssertJ
pytest-mockpytest 的 mock 插件Mockito(注解注入风格)
pytest-cov覆盖率统计JaCoCo
hypothesis基于属性的测试jqwik
factory_boy测试数据工厂EasyRandom / TestContainers
responses/httpx_mockHTTP MockWireMock

3. unittest:标准库的基石

3.1 基本结构

importunittestclassCalculator:defadd(self,a:int,b:int)->int:returna+bdefdivide(self,a:int,b:int)->float:ifb==0:raiseValueError("除数不能为零")returna/bclassTestCalculator(unittest.TestCase):"""类似 JUnit 的 @Test 注解,这里用方法名 test_ 前缀标识测试方法"""defsetUp(self):"""每个测试方法执行前调用,类似 JUnit 的 @BeforeEach"""self.calc=Calculator()deftearDown(self):"""每个测试方法执行后调用,类似 JUnit 的 @AfterEach"""passdeftest_add(self):self.assertEqual(self.calc.add(1,2),3)deftest_divide(self):self.assertAlmostEqual(self.calc.divide(10,3),3.333,places=3)deftest_divide_by_zero(self):withself.assertRaises(ValueError)asctx:self.calc.divide(1,0)self.assertIn("除数不能为零",str(ctx.exception))if__name__=="__main__":unittest.main()

3.2 生命周期方法对照

Python (unittest)Java (JUnit 5)说明
setUp@BeforeEach每个测试前执行
tearDown@AfterEach每个测试后执行
setUpClass@BeforeAll整个类只执行一次
tearDownClass@AfterAll整个类只执行一次

3.3 常用断言

self.assertEqual(a,b)# assertEqualsself.assertTrue(x)# assertTrueself.assertIsNone(x)# assertNullself.assertIn(item,container)# assertTrue(list.contains(item))self.assertRaises(Exception)# assertThrowsself.assertAlmostEqual(a,b)# 浮点近似比较(JUnit 中需手动指定 delta)

4. unittest.mock:Python 内置的 Mock 利器

从 Python 3.3 开始,unittest.mock被纳入标准库。它提供了MockMagicMockpatch三个核心工具。

4.1 Mock 与 MagicMock

fromunittest.mockimportMock,MagicMock# Mock:基础 mock 对象,访问任意属性/方法都会返回新的 Mockm=Mock()m.some_method(1,2,3)m.some_method.assert_called_once_with(1,2,3)# 验证调用# MagicMock:继承 Mock,额外支持魔术方法(__len__、__iter__ 等)mm=MagicMock()mm.__len__.return_value=5len(mm)# 返回 5

类比 JavaMock类似 Mockito 的mock(SomeClass.class),所有方法默认返回 null/0/false。Python 的 Mock 更激进——访问任何属性都不会报错,直接返回一个新 Mock 对象。

4.2 配置返回值与副作用

fromunittest.mockimportMock# 设置返回值api_client=Mock()api_client.get_user.return_value={"id":1,"name":"Alice"}# 设置多次不同返回值api_client.get_user.side_effect=[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},]# 设置抛出异常api_client.get_user.side_effect=ConnectionError("网络超时")# 设置为可调用函数(动态返回)api_client.get_user.side_effect=lambdauser_id:{"id":user_id,"name":f"User_{user_id}"}

4.3 patch:替换目标对象(核心能力)

patch是 Python Mock 体系中最重要也最容易踩坑的功能。它用于在测试期间临时替换指定模块中的对象

# services.pyimportrequestsdefget_weather(city:str)->str:response=requests.get(f"https://api.weather.com/{city}")data=response.json()returnf"{city}:{data['temp']}°C"
# test_services.pyfromunittest.mockimportpatch,Mockfromservicesimportget_weatherclassTestGetWeather:@patch("services.requests.get")# 注意:patch 的是 services 模块中的 requests.getdeftest_get_weather(self,mock_get):# 配置 mock 响应mock_response=Mock()mock_response.json.return_value=
http://www.cnnetsun.cn/news/2855564.html

相关文章:

  • 【3.1Java基础】Java运算符常见错误排查:10个高频编译运行错误一网打尽
  • 还在用老版本jQuery?手把手教你复现CVE-2020-11022/11023这个XSS漏洞(附完整PoC)
  • 别再死记公式!用Python模拟带你直观理解停止等待与回退N帧协议的信道利用率
  • 考研摆烂后如何一周突击复试?北邮网安复试准备全流程(含密码学、408速成法)
  • 新手避坑指南:用大疆NAZA-LITE飞控组装F450无人机,从焊接电调到GPS校准的完整流程
  • ARM9微控制器LPC292x硬件设计实战:从数据手册到可靠电路
  • 从一次线上数据泄露事故复盘:我们是如何用签名和脱敏堵住越权漏洞的
  • 工业数据上云的‘翻译官’:实测KepOPC DA2UA如何桥接Windows OPC DA与跨平台应用
  • 别再傻傻分不清!用猫狗猪分类的例子,一次搞懂论文里的OA、mAcc、Instance和Class Accuracy
  • 动态群组密钥管理协议:原理、实现与优化
  • 不只是玩具:用金牛座脑波模块+ESP32,打造一个低成本的居家专注力监测‘小黑盒’
  • 告别盲目搜索:手把手教你用Keil MDK调试RT-Thread的RT_ASSERT死机问题
  • Arma3任务制作者必看:如何用SQF的ForEach和WaitUntil,让AI小队执行复杂巡逻逻辑
  • 语音RAG实战:构建端到端音频理解与原声回答系统
  • 告别IP依赖:在Vivado中直接调用MMCME2_ADV原语生成自定义时钟(以Zynq-7000为例)
  • 从零配置到上线:手把手带你用华为AC+AP搭建一个可用的企业Wi-Fi(含CAPWAP隧道详解)
  • 别让DRC吓到你!Cadence SPB17.4原理图检查的‘白名单’与‘黑名单’设置心得
  • 别再套模板了!我用这3个真实案例拆解GIS/遥感专业保研个人陈述怎么写(附避坑指南)
  • 别再用暴力搜索了!用动态规划5分钟搞定‘蚂蚁移动’这类网格路径问题(附C++代码)
  • 上市公司财报AI解析流水线:本地化、可验证、零API依赖
  • 用C++队列模拟流感传播:从NOI真题到游戏地图感染算法实战
  • AI简历优化:三重信号编码法突破ATS筛选
  • 别再只看GPS信号格了!手把手教你读懂手机/车载导航里的DOP值(精度衰减因子)
  • 别再死磕TII投稿了!我用LaTeX搞定IEEE论文格式的血泪经验(附模板下载与避坑清单)
  • OpenLayers测距踩坑记:从EPSG:4326坐标偏差到Vue中内存泄漏的排查与修复
  • GeoServer权限进阶:不用账号密码,用AuthKey插件实现API密钥式鉴权(2.25.2 Docker版)
  • 模板驱动型文档自动化:结构化内容生成的核心原理与实践
  • 你的Vue/React老项目可能中招了!排查并修复jQuery 3.5.0以下版本的XSS隐患
  • Android系统定制:如何隐藏开发者模式入口,并用计算器输入%147%+来开启(附完整代码)
  • NXP LPC55S6x双核MCU实战:从TrustZone安全到低功耗设计