Python自动化测试:pytest新手快速入门指南

张开发
2026/4/14 23:04:39 15 分钟阅读

分享文章

Python自动化测试:pytest新手快速入门指南
目录​编辑pytest安装版本匹配是关键核心基础pytest用例运行规则命令行参数与配置文件必学技能前后置操作与断言前后置操作解决初始化问题断言验证测试结果的核心pytest参数化设计pytest.mark.parametrize场景1用例上使用参数化场景2类上使用参数化所有方法共用参数pytest核心fixture的使用重中之重7.1 基本使用7.2 嵌套使用7.3请求多个fixture7.4核心特性yield fixture前置后置7.5 fixture参数灵活控制作用范围7.6 全局fixtureconftest.pypytest安装版本匹配是关键学习pytest的第一步就是安装这里要注意版本兼容性避免因版本问题导致后续使用异常。安装命令很简单在命令行输入以下代码即可pip install pytest8.3.2如果你的Python版本低于3.8可以参考以下版本对应关系选择合适的pytest版本pytest 版本最低 Python 版本8.03.87.13.76.2 - 7.03.65.0 - 6.13.53.3 - 4.62.7, 3.4安装完成后记得在PyCharm中更新Python解释器确认pytest已成功安装。这里有个明显的对比未安装pytest时运行测试用例需要手动编写main函数调用安装后测试方法名前会出现直接运行标志无需额外代码效率大幅提升。核心基础pytest用例运行规则安装完成后并不是所有方法都能被pytest识别并运行必须遵循以下3条核心规则重点记文件名必须以test_开头或者_test结尾如test_login.py、login_test.py测试类必须以Test开头并且不能有__init__方法测试方法必须以test开头如test_login_success、test_get_user_info。这里有个常见坑测试类中不能定义__init__方法。因为pytest采用自动发现机制收集测试用例会自动实例化测试类若定义__init__方法会掩盖测试逻辑引入副作用影响测试结果准确性。如果测试类需要初始化操作无需使用__init__可以选择setup_method/teardown_method、setup_class/teardown_class方法或后续会讲到的fixture函数这些都是更合适的替代方案。示例错误示范含__init__方法class Test(): def __init__(self): print(-----init-------) def test_a(self): print(-----test_a----)运行该代码会出现异常无法正常执行测试用例大家一定要避开这个错误。命令行参数与配置文件pytest提供了丰富的命令行参数能灵活控制测试用例的执行以下是新手最常用的几个参数建议牢记命令描述备注pytest在当前目录及其子目录中搜索并运行测试无pytest -v增加输出的详细程度无pytest -s显示测试中的print语句无pytest 文件名.py运行指定的测试模块如pytest test_login.pypytest -k 关键字只运行测试名包含指定关键字的用例如pytest -k loginpytest --htmlreport.html生成HTML格式的测试报告需安装pytest-html插件实操示例简单运行所有符合规则的用例pytest不显示print内容详细输出并显示print内容pytest -sv-s和-v可连写指定文件运行pytest cases/test_01.py指定具体用例运行pytest cases/test_01.py::Test::test_a。但如果每次运行都要输入长长的命令会非常繁琐。解决方案是创建pytest配置文件pytest.ini将常用参数统一配置后续只需输入pytest命令即可运行。配置文件示例核心配置将该文件放在项目根目录下运行pytest命令时会自动读取配置无需再手动输入参数极大提升效率。必学技能前后置操作与断言前后置操作解决初始化问题测试过程中经常需要在执行用例前做初始化如打开浏览器、连接数据库执行后做清理如关闭浏览器、断开数据库这就是前后置操作。pytest提供3种方式实现其中fixture是最推荐的方式。setup_method 和 teardown_method针对每个测试方法执行一次前置和后置如每个用例前都登录setup_class 和 teardown_class针对整个测试类只执行一次前置和后置如类开始前初始化数据库结束后关闭fixture灵活度最高可实现全局、局部的前后置还能实现数据共享后续详细讲解。示例class Testcase02(): def setup_method(self): print(setup_method) def test01(self): print(test01) def test02(self): print(test02) def teardown_method(self): print(teardown_method) class Test(): def setup_class(self): print(setup_class) def teardown_class(self): print(teardown_class) def test01(self): print(test01) def test02(self): print(test02)运行结果会显示在含setup_method 和 teardown_method方法的测试类中每个测试方法执行前都会打印前置信息执行后打印后置信息。在含有setup_class 和 teardown_class方法的测试类中针对整个测试类只执行一次前置和后置。断言验证测试结果的核心断言是自动化测试的核心用于验证实际结果是否符合预期。pytest无需额外导入断言库直接使用Python原生的assert语句即可语法简洁功能强大。基本语法assert 条件, 错误信息错误信息可选断言失败时显示条件必须是⼀个布尔表达式常见断言场景示例import requests import pytest def test(): #断言基本数据类型 a 1 b 1 assert a 1 str1 kiku str2 kiki assert str1 str2 def test_ds(): # 断⾔列表 expect_list [1, apple, 3.14] actual_list [1, apple, 3.14] assert expect_list actual_list # 断⾔元组 expect_tuple (1, apple, 3.14) actual_tuple (1, apple, 3.14) assert expect_tuple actual_tuple # 断⾔字典 expect_dict {name: Alice, age: 25} actual_dict {name: Alice, age: 25} assert expect_dict actual_dict # 断⾔集合 expect_set {1, 2, 3, apple} actual_set {1, 2, 3, apple} assert expect_set actual_set #测试接口返回数据所有内容字段字段值 def test2(): url https://jsonplaceholder.typicode.com/posts/1 r requests.get(url url) expect_data { userId: 1, id: 1, title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit, body: quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto } actual_data r.json() assert expect_data actual_data #对关键字段进行校验 def test3(): url https://jsonplaceholder.typicode.com/comments?postId1 r requests.get(url url) assert r.json()[0][id] 1 def test4(): url https://jsonplaceholder.typicode.com/ text Use your own data r requests.get(url) assert text in r.text上面代码中用到的网站http://jsonplaceholder.typicode.com/是一个免费、公开、开箱即用的在线模拟 REST API 服务专门用来给开发者做测试、学习、演示、原型开发。它能提供现成的假接口 假数据让我们在没有真实后端的情况下也能练接口、写代码、做自动化测试。断言失败时pytest会清晰显示失败原因方便我们快速定位问题这也是pytest的一大优势。pytest参数化设计pytest.mark.parametrize在测试中经常需要用不同的参数重复运行同一个测试用例如测试登录功能输入不同的账号密码如果手动编写多个用例会造成代码冗余。pytest内置的pytest.mark.parametrize装饰器可轻松实现参数化大幅提升测试效率。常见使用场景场景1用例上使用参数化#对多组参数实现参数化 pytest.mark.parametrize(input, expected,[(35, 8), (16/4, 4), (4*9,37)]) def test7(input, expected): assert eval(input) expected该用例会自动运行3次分别使用3组参数无需编写3个独立用例。场景2类上使用参数化所有方法共用参数#在类上实现参数化 pytest.mark.parametrize(n,expected, [(1, 2), (3, 4)]) class TestClass: def test_simple_case(self, n, expected): assert n 1 expected def test_weird_simple_case(self, n, expected): assert (n * 1) 1 expected要对模块中的所有测试进行参数化还可以将 pytestmark 全局变量赋值#全局变量参数化 pytestmark pytest.mark.parametrize(data,(1,2)) class TestA(): def testA1(self, data): print(data) def testA2(self, data): print(data) class TestB(): def testB1(self, data): print(data) def testB2(self, data): print(data)还可以自定义参数化数据源def data_provider(): #... return [a,b,100] pytest.mark.parametrize(data, data_provider())() def test_data(data): print(data)结合fixture还能实现更复杂的参数化满足不同测试场景需求。pytest核心fixture的使用重中之重fixture是pytest最强大的功能它可以实现前后置操作、数据共享、资源管理等比setup/teardown系列方法更灵活、更强大。7.1 基本使用使用pytest.fixture装饰器标记一个函数该函数就是fixture测试用例只需将fixture函数名作为参数即可调用fixture。#使用fixture来调用方法 pytest.fixture def login(): print(登录login()) def test_blogList(login): print(博客列表页blogList()) def test_blogDetail(login): print(博客详情页blogDetail())运行结果会显示两个测试用例执行前都会先执行login fixture的前置操作实现了代码复用。7.2嵌套使用fixture也能嵌套使用#fixture的嵌套 pytest.fixture def first(): return a pytest.fixture def second(first): return [first] def test_string(second): second.append(b) assert second [a, b]7.3请求多个fixture测试和 fixture 不仅限于⼀次请求单个 fixture 它们可以请求任意多个。#请求多个fixtrue class Fruit: def __init__(self, name, color): self.name name self.color color def __eq__(self, other): return self.name other.name pytest.fixture def my_fruit(): return Fruit(apple, red) pytest.fixture def all_fruits(my_fruit): return [Fruit(apple, green) , Fruit(banana,yellow)] def test_fruit(my_fruit, all_fruits): assert my_fruit in all_fruits7.4核心特性yield fixture前置后置使用yield关键字可在fixture中同时实现前置和后置操作yield之前的代码是前置yield之后的代码是后置测试用例执行完成后会自动执行后置代码实现资源清理。#yield fixtrue pytest.fixture def operator(): print(前置操作数据的初始化) yield 100 print(后置操作数据的清理) def test_01(operator): assert operator 100 print(100operator) def test_02(operator): print(100-operator)常用场景打开/关闭文件、连接/断开数据库等避免资源泄露。“Yield” fixture 使用 yield 而不是 return 。有了这些 fixture 我们可以运行一些代码并将对象返回给请求的 fixture/test 就像其他 fixture ⼀样。唯⼀的不同是• return 被替换为 yield 。• 该 fixture 的任何拆卸代码放置在 yield 之后。⼀旦 pytest 确定了 fixture 的线性顺序它将运行每个 fixture 直到它返回或 yield 然后继续执行列表中的下⼀个 fixture 做同样的事情。测试完成后 pytest 将逆向遍历 fixture 列表对于每个 yield 的 fixture 运行yield语句之后的代码7.5 fixture参数灵活控制作用范围fixture可通过参数控制作用范围、自动调用等核心参数如下pytest.fixture(scope, params, autouse, ids, name)scope作用范围默认function表示每个用例调用一次class每个类调用一次module每个模块(文件)调用一次session整个测试会话调用一次autouse是否自动调用默认False需手动传入True所有用例自动调用无需传入params参数化fixture实现多组参数测试name给fixture重命名调用时需使用新名称。示例scope范围function和classimport pytest pytest.fixture(scopefunction) def fixture_01(): print(初始化) yield print(清理) pytest.fixture(scopeclass) def fixture_02(): print(初始化) yield print(清理) class Testcase: def test_01(self,fixture_01): print(第1个测试用例) def test_02(self,fixture_01): print(第2个测试用例) def test_03(self,fixture_02): print(第1个测试用例) def test_04(self,fixture_02): print(第2个测试用例)可以看到test_01和 test_02方法都各自执行了前置和后置操作因为他们fixture的范围scope是function而 test_03和 test_04在一个类中 并且fixture范围scope是class所以fixture只会在第一个方法开始前执行一次在最后一个方法结束后执行一次。运行后会发现无需手动传入fixture测试类的所有用例都会自动执行前置和后置操作。另外两个scope范围在7.6中展示。通过 params 实现参数化示例pytest.mark.parametrize(data, (1,2,kiku)) def test_provider1(data): print(data) pytest.fixture(params[1,2,kiku]) def provider2(request): return request.param def test_provider2(provider2): print(provider2)我们可以看到通关fixture的params实现参数化的效果和用pytest.mark.parametrize实现的效果相同。7.6 全局fixtureconftest.py如果多个测试模块需要共用fixture无需重复编写可创建conftest.py文件名称固定不能修改将全局fixture放在该文件中所有子目录的测试用例均可直接调用无需导入。可以在项目中的不同目录下创建多个 conftest.py 文件每个 conftest.py 文件都会对其所在目录及其子目录下的测试模块生效。在不同模块的测试中需要用到 conftest.py 的前后置功能时不需要做任何的import导入操作作用可以在不同的 .py 文件中使用同⼀个 fixture 函数。conftest.py文件范围为moduleautouse为True 用例自动调用不用手动传入import pytest pytest.fixture(scopemodule,autouseTrue) def fixture_01(): print(初始化) yield print(清理)文件1def test_01(fixture_01): print(第1个测试用例) class Testcase2: def test_02(self,fixture_01): print(第2个测试用例)文件2def test_01(): print(第1个测试用例) class Testcase1: def test_02(self): print(第2个测试用例)测试结果我们看到scope为moudle时每个课执行的测试文件都调用了一次fixture前置操作和后置操作。conftest.py文件范围为sessionimport pytest pytest.fixture(scopesession, autouseTrue) def fixture_01(): print(初始化) yield print(清理)测试文件内容不变还是上面那两个执行测试我们可以看到整个测试会话中fixture前置操作、后置操作都只执行了一次。所有 .py 文件都在一个会话里顺序按文件名跑前后置只在最开始和最后各跑一次。

更多文章