Pytest接口自动化1-从入门到进阶实战
微信:supertime2018 申请暗号:博客园扶摇
正文
一、为什么要用Pytest来做接口自动化
1.Pytest的用途及优点
Pytest目前是自动化业界非常主流的一个自动化测试框架,它本质是Python的一个第三方单元测试库。和unittest一样,主要是用来管理自动化测试用例的执行的,比如用例执行,用例分组,执行日志输出等等。
Pytest的优点:
1.他可以自动的识别测试模块(测试文件)、测试类以及测试函数,规则很简单:
文件名:格式为test _ *.py或* _test.py的文件
类:Test开头
测试函数/方法:拥有test前缀的测试函数或方法
2.模块化夹具fixture可用来管理各类测试资源
3.对unittest完全兼容
我们知道在pytest出来之前,unittest应用也极为广泛,因此完全兼容unittest,在想要对自动化框架升级的时候可以节省不少开发成本。
4.Pytest是最能 装“插”的开源单元测试框架
https://docs.pytest.org/en/latest/reference/plugin_list.html
目前pytest已经有900多个插件,每天都会新增1到2个插件。著名的插件有:
pytest-allure、pytest-rerunfailures、pytest-xdist等等
插件能力:集成功能强大的测试报告组件,实现失败重跑,并发用例执行等
插件的安装也非常简单,基本上就是:pip install 插件名
5.pytest安装
1)安装命令
pip install pytest
2)验证安装
pytest --version
3)官方文档
https://docs.pytest.org/en/latest/contents.html
以上这些,充分说明pytest是一个简单、易用并且功能强大的用例管理框架。
二、接口测试简介
接口组成元素:
1.接口地址(url+端口+路径)
2.接口请求方式: post get delete put...
3.接口请求参数
4.响应数据
这里演示使用postman完成登陆接口调用:
然后我们看到登陆成功后,响应报文里头有个token,这个东西是干嘛的呢,简单介绍一下:
token用途简介
登陆系统 = 进医院大门
token = 绿码
拥有了绿码才有进入医院的权限 = 拥有token才能访问系统内部的各个页面
没有绿码,不允许进入任何门诊 = 没有token,即使你拥有接口的链接,响应也会提示你鉴权失败
以上只是接口最简单的一个入门介绍,要真正的学好接口测试,要学的东西很多,比如必须熟悉的入门级的基础理论:接口通信原理、HTTP网络协议、接口鉴权机制等等,一节课肯定是没法熟悉接口自动化的,必须系统的去学习才能完整的掌握
三、用Python代码实现接口测试
相对于工具,用python做接口自动化的优势:
扩展性更强,更灵活
python 各种封装、调用
可集成各种库和工具,满足各种需求
allure 报告
jsonpath 报文解析
jenkins 持续集成
。。。
代码实操:Pytest_intf_advance/base_intf/demo.py
PS:这里的接口地址是自己本地搭建的一个接口测试服务,仅供大家参考,后期有时间再弄个免费的公网接口服务给大家使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @File : demo.py import json import jsonpath import requests from Pytest_intf_advance.base_intf.api_key import ApiKey # 接口请求的模拟 # 数据的生成 data = { 'username' : 'admin' , 'password' : '123456' } # 接口的地址 url = 'http://127.0.0.1:5000/api/login' # 将数据传递到对应的接口地址,来实现一次该接口的请求下发并返回响应结果:定义对应的请求方法 res = requests.post(url = url, json = data) # 输出响应结果:编译后的内容 print (res.text) # 返回报文中的某个key,比如msg来获取相应的值 print ( type (res.text)) # 输出响应结果:字典类型 print (res.json()) print ( type (res.json())) # 接口断言 assert 'success' = = res.json()[ 'msg' ] # 接口断言,嵌套字典,{key:value,key:{key:value}} # assert 'changsha' == res.json()['city'] assert 'changsha' = = res.json()[ 'adress' ][ 'city' ] # 那么这里我们就可以用到jsonpath库,来简化取值操作 # jsonpath获取数据的表达式:成功则返回list,失败则返回false # loads是将json格式的内容转换为字典的格式 # jsonpath接收的是dict类型的数据 value_list = jsonpath.jsonpath(res.json(), '$..{0}' . format ( 'city' )) print (value_list) value = value_list[ 0 ] print (value) assert 'changsha' = = value # 就有点繁琐,因此我们引入接口关键字封装,来简化代码,封装api_key # 实例化工具类 ak = ApiKey() assert 'changsha' = = ak.get_text(res.text, 'city' ) print (ak.get_text(res.text, 'city' )) |
代码执行输出信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | { "adress" : { "city" : "changsha" }, "httpstatus" : 200 , "info" : { "age" : 18 , "name" : "admin" }, "msg" : "success" , "token" : "23657DGYUSGD126731638712GE18271H" } < class 'str' > { 'adress' : { 'city' : 'changsha' }, 'httpstatus' : 200 , 'info' : { 'age' : 18 , 'name' : 'admin' }, 'msg' : 'success' , 'token' : '23657DGYUSGD126731638712GE18271H' } < class 'dict' > [ 'changsha' ] changsha changsha Process finished with exit code 0 |
四、接口关键字封装
代码实操:Pytest_intf_advance/base_intf/api_key.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @File : api_key.py """ 这是接口关键字驱动类,用于提供自动化接口测试的关键字方法。 主要实现常用的关键字内容,并定义好所有的参数内容即可 """ import json import allure import jsonpath import requests class ApiKey: # 基于jsonpath获取数据的关键字:用于提取所需要的内容 def get_text( self ,data,key): # jsonpath获取数据的表达式:成功则返回list,失败则返回false # loads是将json格式的内容转换为字典的格式 # jsonpath接收的是dict类型的数据 dict_data = json.loads(data) value = jsonpath.jsonpath(dict_data, '$..{0}' . format (key)) return value[ 0 ] # get请求的封装:因为params可能存在无值的情况,存放默认None def get( self ,url,params = None , * * kwargs): return requests.get(url = url,params = params, * * kwargs) #post请求的封装:data也可能存在无值得情况,存放默认None def post( self ,url,data = None , * * kwargs): return requests.post(url = url,data = data, * * kwargs) if __name__ = = '__main__' : ak = ApiKey() data = { 'username' : 'admin' , 'password' : '123456' } res2 = ak.post(url = 'http://127.0.0.1:5000/api/login' ,json = data) print (res2.text) |
代码执行输出信息:
1 2 3 4 5 6 7 8 9 10 11 12 | { "adress" : { "city" : "changsha" }, "httpstatus" : 200 , "info" : { "age" : 18 , "name" : "admin" }, "msg" : "success" , "token" : "23657DGYUSGD126731638712GE18271H" } |
那么我们第一个接口自动化代码就写完了,我们可以看到,上述代码是不是很松散。没有以一个用例的形式来进行,那么这时候我们就可以上Pytest了
五、用Pytest框架编写接口自动化测试代码
1.基本用例组织
pytest用例运行测规则很简单,从上往下,完全按你放置的顺序来执行
代码实操:Pytest_intf_advance/pytest_intf/case/test_shopXo.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @File : test_shopXo.py import pytest import requests from Pytest_intf_advance_V2.base_intf.api_key import ApiKey class Test_ApiCase(): # 登陆接口用例 def test_login( self ): url = 'http://127.0.0.1:5000/api/login' userInfo = { 'username' : 'admin' , 'password' : '123456' } res = self .ak.post(url = url, json = userInfo) print (res.text) # 获取响应中的结果,用于校验是否成功 msg1 = self .ak.get_text(res.text, 'msg' ) print (msg1) assert msg1 = = 'success' # 查询用户信息接口 def test_getuserinfo( self ,token_fix): # 1.获取工具类、token ak, token = token_fix # 2.查询个人用户信息 url = 'http://127.0.0.1:5000/api/getuserinfo' headers = { 'token' : token } res1 = ak.get(url = url, headers = headers) print (res1.text) name = ak.get_text(res1.text, 'nikename' ) assert "风清扬" = = name if __name__ = = '__main__' : # -s参数,在控制台输出打印信息 # -v参数,在控制台输出详细信息 pytest.main([ '-s' , '-v' , 'test_shopXo_02.py' ]) |
代码执行输出信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | = = = = = = = = = = = = = = = = = = = = = = = = = = = = = test session starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - - Python 3.8 . 10 , pytest - 6.1 . 2 , py - 1.9 . 0 , pluggy - 0.13 . 1 - - D:\Python38\python.exe cachedir: .pytest_cache metadata: { 'Python' : '3.8.10' , 'Platform' : 'Windows-10-10.0.19044-SP0' , 'Packages' : { 'pytest' : '6.1.2' , 'py' : '1.9.0' , 'pluggy' : '0.13.1' }, 'Plugins' : { 'allure-pytest' : '2.8.11' , 'forked' : '1.1.3' , 'html' : '3.0.0' , 'metadata' : '1.8.0' , 'ordering' : '0.6' , 'parallel' : '0.1.0' , 'rerunfailures' : '9.1.1' , 'xdist' : '1.31.0' }, 'JAVA_HOME' : 'C:\\Program Files\\Java\\jdk1.8.0_152' } rootdir: D:\Pytest_intf_advance_V2\pytest_intf\case plugins: allure - pytest - 2.8 . 11 , forked - 1.1 . 3 , html - 3.0 . 0 , metadata - 1.8 . 0 , ordering - 0.6 , parallel - 0.1 . 0 , rerunfailures - 9.1 . 1 , xdist - 1.31 . 0 collecting ... collected 2 items test_shopXo.py::Test_ApiCase::test_login { "adress" : { "city" : "changsha" }, "httpstatus" : 200 , "info" : { "age" : 18 , "name" : "admin" }, "msg" : "success" , "token" : "23657DGYUSGD126731638712GE18271H" } success PASSED test_shopXo.py::Test_ApiCase::test_getuserinfo { "data" : [ { "nikename" : "王五" , "openid" : "UEHUXUXU78272SDSassDD" , "userbalance" : 5678.9 , "userid" : 17890 , "username" : "admin" , "userpoints" : 4321 } ], "httpstatus" : 200 } PASSED = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2 passed in 0.30s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Process finished with exit code 0 |
2.接口关联
实现接口关联有多种方式:
1)和工具类相似使用setup_class,用的时候加个self就可以了
2)公共变量可以定义一个公共变量放到类里,这样类中的所有用例都可以读取到了
那么上面这两种方式,其实是有局限性的,就是只能在单个类或者单个文件中使用,如果需要在整个项目的多个测试文件中使用,就不行了,现在就给大家着重介绍第三种方式,来实现项目级的token预置
六、fixture+conftest实现项目级token预置
接下来,我们先讲讲fixture,官方的介绍比较复杂,这边我个人总结了下。
1.pytest之fixture介绍
fixture是pytest提供的一个简化的装饰器,可以轻松的复用已定义的函数逻辑(比如登陆,获取token,环境数据预置)等操作。
官方介绍:
https://docs.pytest.org/en/latest/explanation/fixtures.html#about-fixtures
概念很简单,关键是如何用,下面给大家编写一个fixture快速入门的例子
代码实操:Pytest_intf_advance/base_intf/test_fix/test_quick_exam.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #coding=utf-8 import pytest # 通过@pytest.fixture声明这个函数为fixture # 用例前置 @pytest .fixture def first_fix(): # 此处假设有代码逻辑几十行,比如执行登陆操作,获取token之类的 # 返回一个list return [ "a" ] # 将已声明为fixture的函数,填写在参数中,这样fixture函数会在该函数调用之前调用 # 测试用例 def test_string(first_fix): # 用例步骤 # 使用first_fix返回的list添加元素 first_fix.append( "b" ) print (first_fix) if __name__ = = '__main__' : pytest.main([ '-s' ]) |
代码执行输出信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | = = = = = = = = = = = = = = = = = = = = = = = = = = = = = test session starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - - Python 3.8 . 10 , pytest - 6.1 . 2 , py - 1.9 . 0 , pluggy - 0.13 . 1 rootdir: D:\Pytest_intf_advance_V2\base_intf\test_fix plugins: allure - pytest - 2.8 . 11 , forked - 1.1 . 3 , html - 3.0 . 0 , metadata - 1.8 . 0 , ordering - 0.6 , parallel - 0.1 . 0 , rerunfailures - 9.1 . 1 , xdist - 1.31 . 0 collected 3 items test_quick_exam.py [ 'a' , 'b' ] . test_conftest_01\doc1\test_case01.py 开始执行登陆操作 用例一 . test_conftest_01\doc2\test_case02.py 开始执行登陆操作 用例二 . = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 3 passed in 0.07s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Process finished with exit code 0 |
以上就是fixture最基本的概念和用法。这时候,可能有人就想问了,那我想在其他文件中使用这个定义好的fixture怎么办呢?那这就要介绍下fixture的好基友,conftest.py配置文件了
2.conftest.py
conftest.py是pytest特有的本地测试配置文件,在这个文件中定义的Fixture可以在项目中多个文件使用,conftest.py文件名称是固定的,pytest会自动识别该文件,只作用于它所在的目录及子目录。
同样的,编写一个快速入门的代码例子:
代码实操:多文件代码,先看代码结构
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/conftest.py
1 2 3 4 5 6 | #coding=gbk import pytest @pytest .fixture() def fix1(): print ( "\n开始执行登陆操作" ) |
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/doc1/test_case01.py
1 2 3 4 5 | #coding=gbk import pytest def test_case02(fix1): print ( "用例一" ) |
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/doc2/test_case02.py
1 2 3 4 5 | #coding=gbk import pytest def test_case02(fix1): print ( "用例二" ) |
Pytest_intf_advance/base_intf/test_fix/test_conftest_01/main_run.py
1 2 3 4 5 6 | #coding=gbk import pytest if __name__ = = '__main__' : pytest.main([ '-s' ]) |
运行main_run.py文件
代码输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | collected 2 items doc1\test_case01.py 开始执行登陆操作 用例一 . doc2\test_case02.py 开始执行登陆操作 用例二 . = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 2 passed in 0.05s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Process finished with exit code 0 |
fixture+conftest的搭配是非常的有用的,接下来我们来在接口自动化中实战一把,来展现一下这对黄金组合的实力
3.接口中应用fix+conftest实现项目级token预置
1)先在conftest中定义fixture
初始化工具类,登陆,并返回工具类对象和token值
代码实操:多文件代码,先看代码结构
Pytest_intf_advance_V2/pytest_intf/conftest.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @File : conftest.py from random import random from Pytest_intf_advance.base_intf.api_key import ApiKey import pytest # @pytest.fixture(scope='session') # def token_fix(): # a = random() # print(a) # return a #项目级fix,整个项目只初始化一次 @pytest .fixture(scope = 'session' ) def token_fix(): # 初始化工具类 ak = ApiKey() # 定义访问链接‘ url = 'http://127.0.0.1:5000/api/login' # 定义请求用户数据 userInfo = { 'username' : 'admin' , 'password' : '123456' } # 发送post请求 res = ak.post(url = url,json = userInfo) # 获取token token = ak.get_text(res.text, 'token' ) # 返回多个值 return ak,token |
Pytest_intf_advance_V2\pytest_intf\api_keyword\api_key.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @File : api_key.py """ 这是接口关键字驱动类,用于提供自动化接口测试的关键字方法。 主要实现常用的关键字内容,并定义好所有的参数内容即可 """ import json import allure import jsonpath import requests class ApiKey: # 基于jsonpath获取数据的关键字:用于提取所需要的内容 def get_text( self ,data,key): # jsonpath获取数据的表达式:成功则返回list,失败则返回false # loads是将json格式的内容转换为字典的格式 # jsonpath接收的是dict类型的数据 dict_data = json.loads(data) value = jsonpath.jsonpath(dict_data, '$..{0}' . format (key)) return value[ 0 ] # get请求的封装:因为params可能存在无值的情况,存放默认None def get( self ,url,params = None , * * kwargs): return requests.get(url = url,params = params, * * kwargs) #post请求的封装:data也可能存在无值得情况,存放默认None def post( self ,url,data = None , * * kwargs): return requests.post(url = url,data = data, * * kwargs) if __name__ = = '__main__' : ak = ApiKey() # res = ak.get(url='http://127.0.0.1:5000/api/getuserinfo',timeout=0.1) # print(res.text) data = { 'username' : 'admin' , 'password' : '123456' } res2 = ak.post(url = 'http://127.0.0.1:5000/api/login' ,json = data) print (res2.text) |
Pytest_intf_advance_V2\pytest_intf\case\test_shopXo_02.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #!/usr/bin/env python # -*- coding: utf-8 -*- # @File : test_shopXo.py import pytest import requests from Pytest_intf_advance_V2.base_intf.api_key import ApiKey class Test_ApiCase(): # 查询用户信息接口 def test_getuserinfo( self ,token_fix): # 1.获取工具类、token ak, token = token_fix # 2.查询个人用户信息 url = 'http://127.0.0.1:5000/api/getuserinfo' headers = { 'token' : token } res1 = ak.get(url = url, headers = headers) print (res1.text) name = ak.get_text(res1.text, 'nikename' ) assert "张三" = = name if __name__ = = '__main__' : # -s参数,在控制台输出打印信息 # -v参数,在控制台输出详细信息 pytest.main([ '-s' , '-v' , 'test_shopXo_02.py' ]) |
代码输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | collecting ... collected 1 item test_shopXo_02.py::Test_ApiCase::test_getuserinfo { "data" : [ { "nikename" : "张三" , "openid" : "UEHUXUXU78272SDSassDD" , "userbalance" : 5678.9 , "userid" : 17890 , "username" : "admin" , "userpoints" : 4321 } ], "httpstatus" : 200 } PASSED = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 1 passed in 0.20s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = Process finished with exit code 0 |
通过fixture+conftest 这套组合,可以方便的在多个文件中使用同一个token,可以在项目内实现更大范围内的接口关联。
本文来自博客园,作者:测试老宅男扶摇,转载请注明原文链接:https://www.cnblogs.com/cekailsf/p/15919246.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架