软件测试通常按阶段划分为四个主要层次,各阶段对应不同的测试对象和目标,形成从局部到整体、从开发到交付的完整质量保障体系:
单元测试(Unit Testing):针对代码中最小可测单元(如函数、方法或类)进行验证,通常由开发人员编写,使用模拟(Mock)或桩(Stub)隔离依赖,确保逻辑正确、边界条件覆盖充分。
集成测试(Integration Testing):在单元测试通过后,将已测试的模块按设计方式组合(如自底向上、自顶向下或大爆炸式),重点检验模块间接口、数据传递、调用逻辑及全局资源(如数据库连接、共享内存)的协同行为。
系统测试(System Testing):将集成后的完整软件系统置于接近真实运行环境(含硬件、网络、第三方服务等)中,依据需求规格说明书(SRS)全面验证功能、性能、安全性、兼容性、可靠性等非功能与功能需求。
验收测试(Acceptance Testing):由用户、客户或业务方主导,基于用户故事、业务流程或合同条款开展,包括Alpha测试(内部用户)、Beta测试(外部真实用户)及UAT(用户验收测试),核心目标是确认系统“是否做对了正确的事”,具备上线交付条件。
此外,按测试类型还可细分为:功能测试、性能测试、安全测试、兼容性测试、回归测试、冒烟测试、探索性测试等,这些类型可贯穿各测试阶段,服务于不同质量目标。
# 示例:单元测试片段(Python + pytest)defadd(a,b):returna+bdeftest_add_positive():assertadd(2,3)==5deftest_add_negative():assertadd(-1,-1)==-2在单元测试中,Mock对象用于模拟外部依赖(如数据库、API、文件系统、第三方服务等),从而隔离被测单元,确保测试快速、稳定、可重复且聚焦逻辑本身。有效使用Mock的关键在于:精准模拟行为、避免过度Mock、验证交互而非实现细节。
✅ 有效使用Mock的实践原则:
只Mock不可控/昂贵/慢速的依赖
- 如:HTTP请求(
requests.get)、数据库查询(session.query())、消息队列发送、时间相关函数(datetime.now())等。 - ❌ 不应Mock被测模块内部纯逻辑函数(破坏可测试性本质)。
- 如:HTTP请求(
明确Mock目标与作用域
- 使用
unittest.mock.patch(Python)或jest.mock()(JS)等工具,在最小作用域内打补丁(如仅patch特定模块中的函数,而非全局替换)。
- 使用
设定合理的行为与返回值
mock.return_value:指定返回结果(如模拟成功响应或异常);mock.side_effect:模拟抛出异常、返回不同值序列、或执行自定义逻辑;- 示例(Python):
fromunittest.mockimportpatchimportrequests@patch('requests.get')deftest_fetch_user(mock_get):# 模拟成功响应mock_get.return_value.json.return_value={"id":1,"name":"Alice"}mock_get.return_value.status_code=200result=fetch_user(1)# 被测函数assertresult=={"id":1,"name":"Alice"}mock_get.assert_called_once_with("https://api.example.com/users/1")
验证交互(Assert Interactions)
- 检查依赖是否被正确调用(次数、参数、顺序):
mock.assert_called_once()、mock.assert_called_with(url)、mock.assert_not_called()等。
- 检查依赖是否被正确调用(次数、参数、顺序):
避免“测试脆弱性”
- ❌ 不要Mock太多层级(如Mock了A→B→C,实际只需MockC);
- ❌ 避免断言Mock内部状态(如
mock._mock_called),而应断言业务结果+关键交互。
结合Stub与Spy提升可读性
- Stub:仅提供预设返回值(无行为验证);
- Spy:记录调用信息供后续断言(如
call_args_list); - Mock:兼具Stub + Spy + 行为控制能力。
💡黄金法则:单元测试应像“显微镜”——只观察被测函数的输入输出与关键依赖交互,不关心依赖内部如何工作。