作为一名Python老炮,见证了无数项目因为缺乏测试而崩塌。单元测试就像给代码上保险,帮你在产品上线前揪出潜在Bug。今天聊聊pytest这个测试框架,分享些实战经验。
pytest是Python测试界的当红炸子鸡,比unittest更简洁优雅。来看个最基础的例子:
# calc.pydefadd(a, b):return a + b# test_calc.pydeftest_add():assert add(1,2) ==3assert add(-1,1) ==0assert add(0,0) ==0
运行测试只需要在命令行敲:pytest test_calc.py,pytest会自动发现以test_开头的函数并执行测试。这个例子太简单?来点硬核的。
异常测试是个经常被忽视的重点:
import pytestdefdivide(a, b):if b ==0:raise ValueError("除数不能为0")return a / bdeftest_divide_zero():with pytest.raises(ValueError)as exc_info: divide(1,0)assertstr(exc_info.value) =="除数不能为0"
测试夹具(fixture)是pytest的杀手锏,用来处理测试前后的准备和清理工作:
import pytestimport tempfileimport os@pytest.fixturedeftemp_file():# 准备阶段:创建临时文件 fp = tempfile.NamedTemporaryFile(delete=False) fp.write(b'hello world') fp.close()yield fp.name# 返回文件路径给测试用例# 清理阶段:删除临时文件 os.unlink(fp.name)deftest_read_file(temp_file):withopen(temp_file)as f: content = f.read()assert content =='hello world'
参数化测试可以一次性测试多组数据:
@pytest.mark.parametrize("input,expected", [("hello","HELLO"),("world","WORLD"),("pytest","PYTEST"),])deftest_upper(input, expected):assertinput.upper() == expected
模拟对象(mock)能帮你测试复杂依赖:
from unittest.mockimport patchimport requestsdefget_user_data(user_id): response = requests.get(f"http://api.example.com/users/{user_id}")return response.json()deftest_get_user_data(): mock_response = {"id":1,"name":"老张"}with patch('requests.get')as mock_get: mock_get.return_value.json.return_value = mock_response result = get_user_data(1)assert result == mock_response mock_get.assert_called_once_with("http://api.example.com/users/1")
异步测试也不在话下:
import asyncioimport pytestasyncdefasync_add(a, b):await asyncio.sleep(0.1)# 模拟异步操作return a + b@pytest.mark.asyncioasyncdeftest_async_add(): result =await async_add(1,2)assert result ==3
测试覆盖率是衡量测试完整性的重要指标。安装pytest-cov插件后:
pytest --cov=myproject tests/
还可以生成漂亮的HTML覆盖率报告:
pytest --cov=myproject --cov-report=html tests/
写好的测试用例要有层次感,我习惯这么组织:
classTestUserService:deftest_create_user_success(self):# 测试正常创建用户passdeftest_create_user_duplicate(self):# 测试创建重复用户passdeftest_create_user_invalid_data(self):# 测试无效数据passclassTestOrderService:deftest_place_order_success(self):# 测试下单成功passdeftest_place_order_insufficient_stock(self):# 测试库存不足pass
一些实用的pytest技巧:
-v
显示详细的测试信息
-k
按关键字筛选测试用例
-x
遇到失败立即停止
--pdb
遇到失败时进入调试模式
--durations=N
显示最慢的N个测试用例
测试代码也是代码,同样需要维护。保持测试用例的简洁性和可读性,一个测试函数只测试一个场景。测试数据尽量使用具有代表性的边界值,比如空值、极大值、特殊字符等。
记住:测试不是写完代码后的点缀,而是开发过程中不可或缺的一环。好的测试用例就是最好的文档,能清晰地表达代码的预期行为。养成编写测试的习惯,让你的代码质量更上一层楼。
要我说,就算项目再赶,也得给测试留时间。毕竟,与其在生产环境改Bug,不如在测试阶段就把问题掐死在摇篮里。
领取专属 10元无门槛券
私享最新 技术干货