• 基于Pytest+Requests+Allure实现接口自动化测试!


    一、整体结构

    • 框架组成:pytest+requests+allure
    • 设计模式:
      • 关键字驱动
    • 项目结构:
      • 工具层:api_keyword/
      • 参数层:params/
      • 用例层:case/
      • 数据驱动:data_driver/
      • 数据层:data/
      • 逻辑层:logic/

    二、具体步骤及代码

    1、工具层
    将get、post等常用行为进行二次封装。
    代码(api_key.py)如下:

    1. import allure
    2. import json
    3. import jsonpath
    4. import requests
    5. # 定义一个关键字类
    6. class ApiKey:
    7. # 将get请求行为进行封装
    8. @allure.step("发送get请求")
    9. def get(self, url, params=None, **kwargs):
    10. return requests.get(url=url, params=params, **kwargs)
    11. # 将post请求行为进行封装
    12. @allure.step("发送post请求")
    13. def post(self, url, data=None, **kwargs):
    14. return requests.post(url=url, data=data, **kwargs)
    15. # 由于接口之间可能相互关联,因此下一个接口需要上一个接口的某个返回值,此处采用jsonpath对上一个接口返回的值进行定位并取值
    16. @allure.step("获取返回结果字典值")
    17. def get_text(self, data, key):
    18. # json数据转换为字典
    19. json_data = json.loads(data)
    20. # jsonpath取值
    21. value = jsonpath.jsonpath(json_data, '$..{0}'.format(key))
    22. return value[0]
    • 其中引用allure.step()装饰器进行步骤详细描述,使测试报告更加详细。
    • 使用jsonpath对接口的返回值进行取值。

    2、数据层
    数据采用yaml文件。
    代码(user.yaml)如下:

    1. -
    2. user:
    3. username: admin
    4. password: '123456'
    5. msg: success
    6. title: 输入正确账号、密码,登录成功
    7. -
    8. user:
    9. username: admin1
    10. password: '1234561'
    11. msg: 用户名或密码错误
    12. title: 输入错误账号1、密码1,登录失败
    13. -
    14. user:
    15. username: admin2
    16. password: '1234562'
    17. msg: 用户名或密码错误
    18. title: 输入错误账号2、密码2,登录失败
    • 其中title是为了在用例进行时动态获取参数生成标题。

    3、数据驱动层
    对数据进行读写。
    代码(yaml.driver.py)如下:

    1. import yaml
    2. def load_yaml(path):
    3. file = open(path, 'r', encoding='utf-8')
    4. data = yaml.load(file, Loader=yaml.FullLoader)
    5. return data

    4、参数层
    参数层存放公共使用的参数,在使用时对其进行调用。
    代码(allParams.py)如下:

    1. '''
    2. 规则:
    3. 全局变量使用大写字母表示
    4. '''
    5. # 地址
    6. URL = 'http://39.98.138.157:'
    7. # 端口
    8. PORT = '5000'
    1. 现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
    2. 如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
    3. 可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
    4. 分享他们的经验,还会分享很多直播讲座和技术沙龙
    5. 可以免费学习!划重点!开源的!!!
    6. qq群号:110685036

    5、逻辑层

    用例一:进行登录的接口请求,此处登录请求在yaml文件里设置了三组不同的数据进行请求。

    用例二:进行个人查询的接口请求,此处需要用到登录接口返回的token值。

    用例三、进行添加商品到购物车的接口请求,此处需要用到登录接口返回的token值以及个人查询接口返回的openid、userid值

    用例四、进行下单的接口请求,此处需要用到登录接口返回的token值以及个人查询接口返回的openid、userid、cartid值

    注意:由于多数接口需要用到登录接口返回的token值,因此封装一个conftest.py定义项目级前置fixture,在整个项目只执行一次,可以在各个用例中进行调用(其他共用参数也可以采取类似前置定义)。同时由于此处定义的项目级fixture,因此可以将初始化工具类ak = ApiKey()也放入其中。

    代码(conftest.py)如下:

    1. from random import random
    2. import allure
    3. import pytest
    4. from pytest_demo_2.api_keyword.api_key import ApiKey
    5. from pytest_demo_2.params.allParams import *
    6. def pytest_collection_modifyitems(items):
    7. """
    8. 测试用例收集完成时,将收集到的item的name和nodeid的中文显示在控制台上
    9. """
    10. for item in items:
    11. item.name = item.name.encode("utf-8").decode("unicode_escape")
    12. item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
    13. # 项目级fix,整个项目只初始化一次
    14. @pytest.fixture(scope='session')
    15. def token_fix():
    16. # 初始化工具类
    17. ak = ApiKey()
    18. with allure.step("发送登录接口请求,并获取token,整个项目只生成一次"):
    19. # 请求接口
    20. # url = 'http://39.98.138.157:5000/api/login'
    21. url = URL + PORT + '/api/login'
    22. # 请求参数
    23. userInfo = {
    24. 'username': 'admin',
    25. 'password': '123456'
    26. }
    27. # post请求
    28. res = ak.post(url=url, json=userInfo)
    29. # 获取token
    30. token = ak.get_text(res.text, 'token')
    31. # 验证代码,验证token只生成一次
    32. token_random = random()
    33. return ak, token, res, token_random
    • 其中也包含了防止中文乱码,加入了pytest_collection_modifyitems(函数)。

    设置好conftest后,就可以应用在逻辑层里面了。
    代码(shopingApi.py)如下:

    1. import pytest
    2. import allure
    3. from pytest_demo_2.api_keyword.api_key import ApiKey
    4. from pytest_demo_2.params.allParams import *
    5. class ApiCase():
    6. # 登录逻辑
    7. def params_login(self, userdata):
    8. # 动态获取参数生成标题
    9. allure.dynamic.title(userdata['title'])
    10. # 初始化工具类
    11. ak = ApiKey()
    12. # 请求接口
    13. url = URL + PORT + '/api/login'
    14. # 请求参数
    15. userInfo = {
    16. 'username': userdata['user']['username'],
    17. 'password': userdata['user']['password']
    18. }
    19. res = ak.post(url=url, json=userInfo)
    20. with allure.step("接口返回信息校验及打印"):
    21. print("/api/login登录接口请求响应信息")
    22. print(res.text)
    23. # 获取响应结果
    24. msg = ak.get_text(res.text, 'msg')
    25. print(msg)
    26. # 断言
    27. assert msg == userdata['msg']
    28. def params_getuserinfo(self, token_fix):
    29. # 从fix中获取预置的工具类和token,所有返回值都需要接收
    30. ak, token, res, token_random01 = token_fix
    31. with allure.step("发送个人查询接口请求"):
    32. url = URL + PORT + '/api/getuserinfo'
    33. headers = {
    34. 'token': token
    35. }
    36. res1 = ak.get(url=url, headers=headers)
    37. with allure.step("接口返回信息校验及打印"):
    38. print("/api/getuserinfo个人用户查询接口请求响应信息")
    39. print(res1.text)
    40. # print("验证的random值,测试用")
    41. # print(token_random01)
    42. name = ak.get_text(res1.text, 'nikename')
    43. # 断言
    44. assert "风清扬" == name
    45. return res1
    46. def params_addcart(self, token_fix):
    47. # 从fix中获取预置的工具类和token
    48. # 所有返回都要获取,不然会报错
    49. ak, token, res, token_random01 = token_fix
    50. with allure.step("调用getuserinfo接口获取返回信息"):
    51. res1 = self.params_getuserinfo(token_fix)
    52. with allure.step("发送添加商品到购物车请求"):
    53. # 添加商品到购物车,基于token、userid、openid、productid
    54. url = URL + PORT + '/api/addcart'
    55. hd = {
    56. "token": token
    57. }
    58. data = {
    59. "userid": ak.get_text(res1.text, 'userid'),
    60. "openid": ak.get_text(res1.text, 'openid'),
    61. "productid": 8888
    62. }
    63. # 发送请求
    64. res2 = ak.post(url=url, headers=hd, json=data)
    65. with allure.step("接口返回信息校验及打印"):
    66. print("/api/addcart添加商品到购物车请求响应信息")
    67. print(res2.text)
    68. # print("验证的random值,测试用")
    69. # print(token_random01)
    70. result = ak.get_text(res2.text, 'result')
    71. assert 'success' == result
    72. return res2
    73. def params_createorder(self, token_fix):
    74. ak, token, res, token_random01 = token_fix
    75. with allure.step("调用addcart接口获取返回信息"):
    76. res1 = self.params_addcart(token_fix)
    77. with allure.step("发送下单请求"):
    78. url = URL + PORT + '/api/createorder'
    79. # 从项目级fix中获取token
    80. hd = {
    81. "token": token
    82. }
    83. # 从添加商品到购物车接口中获取userid,openid,cartid
    84. data = {
    85. "userid": ak.get_text(res1.text, 'userid'),
    86. "openid": ak.get_text(res1.text, 'openid'),
    87. "productid": 8888,
    88. "cartid": ak.get_text(res1.text, 'cartid')
    89. }
    90. res2 = ak.post(url=url, headers=hd, json=data)
    91. with allure.step("接口返回信息校验及打印"):
    92. print("/api/createorder下单请求响应信息")
    93. print(res2.text)
    94. # print("验证的random值,测试用")
    95. # print(token_random01)
    96. result = ak.get_text(res1.text, 'result')
    97. assert 'success' == result

    6、用例层
    调用逻辑层进行用例管理和数据传输。
    代码(test_Tree.py)如下:

    1. import allure
    2. import pytest
    3. from pytest_demo_2.data_driver import yaml_driver
    4. from pytest_demo_2.logic.shopingApi import ApiCase
    5. @allure.epic("shopXo电商平台接口-接口测试")
    6. class TestTree():
    7. # 初始化用例库
    8. actions1 = ApiCase()
    9. @allure.feature("01.登陆")
    10. @allure.story("02.一般场景")
    11. @pytest.mark.parametrize('userdata', yaml_driver.load_yaml('./data/user.yaml'))
    12. def test_case01(self, userdata):
    13. self.actions1.params_login(userdata)
    14. @allure.feature("02.个人查询")
    15. @allure.story("01.典型场景")
    16. @allure.title("个人查询")
    17. def test_case02(self, token_fix):
    18. self.actions1.params_getuserinfo(token_fix)
    19. @allure.feature("03.添加商品到购物车")
    20. @allure.story("01.典型场景")
    21. @allure.title("添加商品到购物车")
    22. def test_case03(self, token_fix):
    23. self.actions1.params_addcart(token_fix)
    24. @allure.feature("04.下单")
    25. @allure.story("01.典型场景")
    26. @allure.title("下单")
    27. def test_case04(self, token_fix):
    28. self.actions1.params_createorder(token_fix)

    7、运行
    代码(main_run.py)如下:

    1. import os
    2. import pytest
    3. def run():
    4. pytest.main(['-v', './case/test_Tree.py',
    5. '--alluredir', './result', '--clean-alluredir'])
    6. os.system('allure serve result')
    7. # os.system('allure generate ./result/ -o ./report_allure/ --clean')
    8. if __name__ == '__main__':
    9. run()

    8、结果

    如果觉得有用,就请关注、点赞、在看、分享到朋友圈吧。

  • 相关阅读:
    Java#7(this关键字和构造方法)
    磁选机是什么?
    k8s之Pod
    SpringBoot入门知识
    valarray数值库学习
    python中如何画语谱图
    查找数组中第K大的元素
    力扣刷题 day56:10-26
    【Nuxt3】Vue3 + Element-plus 打包后报错 @popperjs/core
    机器学习-特征映射方法
  • 原文地址:https://blog.csdn.net/qq_43371695/article/details/134091187