• 接口自动化测试小结


    一、接口测试的概念

    1、接口:指系统或组件之间的交互点,通过这些交互点可以实现数据之间的交互。(数据交互的通道)

    2、接口测试:对系统或组件之间的接口进行测试,主要用于检测外部系统与系统之间以及系统内部之间的数据交换传递控制管理过程,以及系统间的相互逻辑依赖关系

    3、接口自动化测试:使用程序或者工具自动的完成对进口进行测试。

    二、接口测试的实现方式

    • 工具:Postman、Jmeter
    • 代码:python + requests

    三、接口测试的流程

    • 需求分析
      • 主要依据是需求文档
    • 挑选需要做接口测试的功能
    • 接口文档解析
    • 设计测试用例
    • 执行测试
      • 使用接口测试工具实现
      • 通过编写代码实现
    • 接口缺陷管理与跟踪
    • 接口自动化持续集成

    四、接口自动化测试的流程

    • 将功能用例抽取转化成自动化用例
    • 搭建测试环境 (工具环境或者代码环境)
    • 使用代码搭建自动化结构目录
    • 编写用例脚本
    • 执行用例脚本
    • 查看测试报告
    • 持续集成

    五、接口测试用例

    ID 系统 模块 用例名称 接口名称 请求URL 请求类型 请求头 请求参数类型 请求参数 预期结果 测试结果
    001 前台系统 登录 登录成功 登录
    http://ttapi.research.itcast.cn
    /mp/v1_0/authorizations
    POST  
    {"Content-Type": "application/json"}
    json
    {"mobile":mobile,"code":code}
     
       
    002 文章 发布文章 发布文章  
    http://ttapi.research.itcast.cn
    /mp/v1_0/articles
    POST  
    {"Content-Type": "application/json",
    "Authorization": "Bearer token"}
    json  
    {"title":title,"content":content,"channel_id":channel_id,"cover":{"type":0,"images":[]}}
       
    003 后台管理系统 登录 登录成功 登录
    http://ttapi.research.itcast.cn
    /mis/v1_0/authorizations
     
    POST  
    {"Content-Type": "application/json"}
    json  
    data = {"account":account,"password":password}
       
    004 文章 查询文章 查询文章  
    http://ttapi.research.itcast.cn
    /mis/v1_0/articles
    GET  
    {"Content-Type": "application/json",
    "Authorization": "Bearer token"}
    QueryString  
    {"title":api.title,"channel":api.channel}
       
    005 审核文章 审核文章  
    http://ttapi.research.itcast.cn
    /mis/v1_0/articles
    PUT  
    {"Content-Type": "application/json",
    "Authorization": "Bearer token"}
    json  
    {"article_ids":[api.article_id],"status":2}
       

    六、项目搭建

    (1) 初始化项目

    • 目录名称

    (2)创建目录结构

    • 目录结构包括:api、scripts、data、log、report、tool

     【关于项目目录结构的说明】

    • api:资源对象接口封装(相当于web自动化测试中的page)
    • scripts:资源接口测试脚本
    • data:数据驱动
    • log:日志
    • report:测试报告
    • tool:工具类

    (3)公共变量的抽取

    通过分析接口测试用例,我们可以提取如下公共变量,分别为:

    • host域名及端口:http://ttapi.research.itcast.cn
    • 请求消息头headers:{"Content-Type": "application/json"}

    为了方便api包下的模块调用提取的公共变量,我们可以将公共变量存放在api包下的__init__.py模块中:

     【以下为本接口自动化测试项目抽取的所有公共变量】

     1 """公共变量"""
     2 from tool.read_yaml import read_yaml
     3 
     4 # 1. 请求域名
     5 host = "http://ttapi.research.itcast.cn"
     6 
     7 # 2. 请求信息头
     8 headers = {"Content-Type": "application/json"}
     9 
    10 # 3. 用于接收服务器返回的文章id,用于文章审核是调用
    11 article_id = None
    12 
    13 # 接收发布文章读取的数据
    14 data_article = read_yaml("mt_article.yaml")
    15 print(data_article)
    16 
    17 # 文章title
    18 title = data_article[0][0]
    19 # 文章内容
    20 content = data_article[0][1]
    21 # 文章所属频道id
    22 channel_id = data_article[0][2]
    23 # 文章所属频道
    24 channel = data_article[0][3]

    (4)资源Api结构搭建及实现

    【前端媒体资源】

    1、前端媒体资源api结构搭建 -- 主要包括初始化以及待测试的各个接口 -- api_mt.py

    • 初始化
      • 定义登录接口url
      • 定义发布文章接口url
    • 登录接口封装
      • 定义请求数据
      • 发送post请求返回响应对象
    • 发布文章接口封装
      • 定义请求数据
      • 发送post请求返回响应对象

    2、前端媒体资源api结构的实现

     【代码具体实现如下】

     1 import api
     2 import requests
     3 from tool.get_log import GetLog
     4 
     5 log = GetLog.get_logger()
     6 
     7 class ApiMt:
     8 
     9     # 1. 初始化
    10     def __init__(self):
    11         # 1. 登录接口url
    12         self.url_login = api.host + "/mp/v1_0/authorizations"
    13         log.info("正在初始化自媒体登录url:{}".format(self.url_login))
    14         # 2. 发布文章接口url
    15         self.url_article = api.host + "/mp/v1_0/articles"
    16         log.info("正在初始化自媒体发布文章url:{}".format(self.url_article))
    17         pass
    18 
    19     # 2. 登录接口
    20     def api_mt_login(self,mobile,code):
    21         """
    22 
    23         :param mobile: 手机号
    24         :param code: 验证码
    25         :return: 响应对象
    26         """
    27         # 1. 定义请求数据
    28         data = {"mobile":mobile,"code":code}
    29         # 2. 调用post方法
    30         log.info("正在调用自媒体登录接口,请求数据:{}".format(data))
    31         return requests.post(url=self.url_login, json=data, headers=api.headers)
    32 
    33     # 3. 发布文章接口
    34     def api_mt_article(self,title,content,channel_id):
    35         """
    36 
    37         :param title: 文章标题
    38         :param content: 文章内容
    39         :param channel_id: 频道id
    40         :param cover: 封面 0: 为自动
    41         :return: 响应对象
    42         """
    43         # 1. 定义请求数据
    44         data = {"title":title,"content":content,"channel_id":channel_id,"cover":{"type":0,"images":[]}}
    45         # 2,调用post方法
    46         log.info("正在调用自媒体发布文章接口,请求数据:{}".format(data))
    47         return requests.post(url=self.url_article, json=data, headers=api.headers)  

    【注意】

    在发送请求出传递请求头参数时,注意headres的拼写

    【后台管理资源】

    1、后台管理资源api结构的搭建 -- 主要包括初始化以及待测试的各个接口 -- api_ht.py

    • 初始化
      • 定义登录url
      • 定义查询文章url
      • 定义审核文章url
    • 登录
      • 定义请求数据
      • 发送post请求返回响应对象
    • 查询文章
      • 定义请求数据
      • 发送get请求返回响应对象
    • 审核文章
      • 定义请求数据
      • 发送put请求返回响应对象

    2、后台管理资源api结构的实现

    【代码具体实现如下】

     1 import api
     2 import requests
     3 from tool.get_log import GetLog
     4 
     5 log = GetLog.get_logger()
     6 
     7 class ApiHt:
     8 
     9     # 初始化: 用于获取各个接口的url
    10     def __init__(self):
    11         # 1. 登录url
    12         self.url_login = api.host + "/mis/v1_0/authorizations"
    13         log.info("正在初始化后台管理系统 登录url:{}".format(self.url_login))
    14         # 2. 查询url
    15         self.url_search = api.host + "/mis/v1_0/articles"
    16         log.info("正在初始化后台管理系统 查询url:{}".format(self.url_search))
    17         # 3. 审核url
    18         self.url_audit = api.host + "/mis/v1_0/articles"
    19         log.info("正在输出化后台管理系统 审核url:{}".format(self.url_audit))
    20 
    21 
    22     # 登录接口
    23     def api_ht_login(self,account,password):
    24         """
    25 
    26         :param account: 账号
    27         :param password: 密码
    28         :return: 响应信息
    29         """
    30         # 1. 参数数据
    31         data = {"account":account,"password":password}
    32         log.info("正在调用后台管理系统登录接口,请求的数据为:{} 请求的信息头为:{}".format(data,api.headers))
    33         # 2. 调用post方法
    34         return requests.post(url=self.url_login,json=data,headers=api.headers)
    35 
    36     # 查询文章接口
    37     def api_ht_search(self):
    38         """
    39         :param title: 文章标题 数据来源于api.__init__.py模块
    40         :param channel: 文章频道 数据来源于api.__init__.py模块
    41         :return:
    42         """
    43         # 1. 参数数据
    44         data = {"title":api.title,"channel":api.channel}
    45         log.info("正在调用后台管理系统查询文章接口,请求的数据为:{} 请求的信息头为:{}".format(data,api.headers))
    46         # 2. 调用get方法
    47         return requests.get(url=self.url_search,params=data,headers=api.headers)
    48 
    49     # 审核文章接口
    50     def api_ht_audit(self):
    51         """
    52         :param article_ids: 文章id,数据来源发布文章后服务器生成的
    53         :param status: 2 为文章审核通过
    54         :return: 响应对象
    55         """
    56         # 1. 参数数据
    57         data = {"article_ids":[api.article_id],"status":2}  # 2 为文章审核通过
    58         log.info("正在调用后台管理系统文章审核接口,请求的数据为:{} 请求的信息头为:{}".format(data,api.headers))
    59         # 2. 调用put方法
    60         return requests.put(url=self.url_audit,json=data,headers=api.headers)

    【说明】

    • 在实现搭建的api结构时,查询文章和审核文章的参数数据都是直接从api包下的__init__.py模块中直接读取的,因为参数数据已通过read_yaml工具读取并存储在__init__.py模块中;
    • 在发送请求时,注意headers单词的拼写。

    (5)测试业务层结构搭建及实现

    【前端媒体资源测试业务】

    1、前端媒体测试业务层搭建

    • 初始化
      • 获取ApiMt对象
    • 登录接口测试方法
      • 调用登录接口
      • 提取响应数据中的token信息
      • 断言(参数化)
    • 发布文章接口测试方法
      • 调用发布文章接口
      • 获取响应中的数据中文章id
      • 断言(参数化)

    2、公共方法的提取

    由于在断言时,前后台接口所涉及的5条测试用例,据需要进行断言操作,且断言操作的内容基本上都是比较响应状态码以及返回数据中的message;

    同时在前后台登录接口中均涉及token数据的提取,因此我们可以将断言和token数据的提取封装成成相应的工具方法,在测试脚本中需要时直接调用工具类中的方法即可。

    代码实现如下:

     1 """
     2 用于提取测试脚本中的公共方法:
     3     - 获取token
     4     - 断言
     5 """
     6 import api
     7 from tool.get_log import GetLog
     8 
     9 log = GetLog.get_logger()
    10 
    11 class Tool:
    12 
    13     @classmethod
    14     def common_token(cls,resp):
    15         # 获取token
    16         token = resp.json()["data"]['token']
    17         # 将token添加到headers中
    18         api.headers["Authorization"] = "Bearer " + token
    19         log.info("正在提取token,提取后的headers为:{}".format(api.headers))
    20         print("添加token后的headers为:", api.headers)
    21 
    22     @classmethod
    23     def common_assert(cls,resp,status_code=201):
    24         log.info("正在调用公共断言方法!")
    25         # 断言响应状态码
    26         assert status_code == resp.status_code
    27         # 断言响应内容
    28         assert "OK" == resp.json()["message"]

    -- 断言操作中默认的响应状态为201,在在实际的测试过程中,如果存在其他的响应状态码,可以在调用该断言方法时进行修改

    3、登录、发布文章测试用例实现

     1 from api.api_mt import ApiMt
     2 from tool.tool import Tool
     3 from tool.get_log import GetLog
     4 from tool.read_yaml import read_yaml
     5 import pytest
     6 import api
     7 
     8 log = GetLog.get_logger()
     9 
    10 import api
    11 class TestMt:
    12 
    13     # 1. 初始化
    14     def setup_class(self):
    15         # 获取ApiMt对象
    16         self.mt = ApiMt()
    17         pass
    18 
    19     # 2. 登录接口测试方法
    20     @pytest.mark.parametrize("mobile,code",read_yaml("mt_login.yaml"))
    21     def test_01_mt_login(self,mobile,code):
    22         # 调用登录接口
    23         resp = self.mt.api_mt_login(mobile=mobile,code=code)
    24         # 打印数据结果
    25         print("登录结果为:", type(resp.json()))
    26         try:
    27             # 提取token
    28             Tool.common_token(resp)
    29             # 断言
    30             Tool.common_assert(resp)
    31         except Exception as e:
    32             # 写日志
    33             log.error("断言错误:{}".format(e))
    34             # 抛异常
    35             raise
    36 
    37         # 以下方法封装成tool
    38         # # 提取token
    39         # token = resp.json()["data"]['token']
    40         # # 将token追加到请求头 -- 保存到api包的__init__.py中
    41         # api.headers["Authorization"] = "Bearer " + token
    42         # print("添加token后的headers为:", api.headers)
    43         # # 断言状态码
    44         # assert 201 == resp.status_code
    45         # # 断言响应信息
    46         # assert "OK" == resp.json()["message"]
    47 
    48     # 3. 发布文章接口测试方法
    49     def test_02_mt_article(self,title=api.title,content=api.content,channel_id=api.channel_id):
    50         # 1. 调用发布文章接口
    51         resp = self.mt.api_mt_article(title,content,channel_id)
    52         # 2. 获取id
    53         api.article_id = resp.json()["data"]['id']   
    54         print("发布文章成功后的id:{}".format(api.article_id))
    55         try:
    56             # 3. 断言
    57             Tool.common_assert(resp)
    58         except Exception as e:
    59             # 1. 日志
    60             log.error("断言失败:{}".format(e))
    61             # 2. 异常抛出
    62             raise

    【说明】

    • 在实现发布文章接口测试方法时,我们需要提取响应数据中文章id的信息,并将其赋值给api包下的__init__.py模块中的article_id变量,因为后台管理系统在查询文章使需要使用到该article_id的值;
    • 在给测试脚本命名时,除了以test开头外,名称中最好不要出现数字。

    【后台管理资源测试业务】

    1、后台管理测试业务层搭建

    • 初始化
      • 获取ApiHt对象
    • 登录测试方法
      • 调用登录接口
      • 提取响应信息中的token值
      • 断言
    • 查询文章测试方法
      • 调用查询文章接口
      • 断言
    • 审核文章测试方法
      • 调用审核文章接口
      • 断言

    2、登录、查询文章以及审核文章测试用例实现

     【测试用例代码实现】

     1 from api.api_ht import ApiHt
     2 from tool.tool import Tool
     3 from tool.get_log import GetLog
     4 from tool.read_yaml import read_yaml
     5 import api
     6 import pytest
     7 
     8 log = GetLog.get_logger()    # 用于记录日志信息
     9 
    10 class TestHt:
    11 
    12     # 1. 初始化
    13     def setup_class(self):
    14         # 获取ApiHt对象
    15         self.ht = ApiHt()
    16         pass
    17 
    18     # 2. 登录
    19     @pytest.mark.parametrize("account,password",read_yaml("ht_login.yaml"))
    20     def test_01_ht_login(self,account,password):
    21         # 调用登录接口
    22         resp = self.ht.api_ht_login(account=account,password=password)
    23         # 提取token
    24         Tool.common_token(resp)
    25         print("后台管理系统登录后,请求的headers为:{}".format(api.headers))
    26         try:
    27             # 断言
    28             Tool.common_assert(resp)
    29         except Exception as e:
    30             # 1. 日志
    31             log.error("断言错误:{}".format(e))
    32             # 2. 抛异常
    33             raise
    34 
    35     # 3. 查询文章
    36     # -- 说明:由于后台无法查看的审核文章的信息,因此在断言时出错,因此导致查询文章和审核文章的用例无法继续向下运行
    37     def test_02_ht_search(self):
    38         # 1. 调用查询文章接口
    39         resp = self.ht.api_ht_search()
    40         print("查询文章的信息为:",resp.json())
    41         try:
    42             # 2. 断言
    43             Tool.common_assert(resp,status_code=200)    # 注意:状态码为200并且为int类型
    44         except Exception as e:
    45             # 日志记录
    46             log.error("断言出错:{}".format(e))
    47             # 抛异常
    48             raise
    49 
    50     # 4. 审核文章
    51     def test_03_ht_audit(self):
    52         # 1. 调用审核文章接口
    53         resp = self.ht.api_ht_audit()
    54         try:
    55             # 2. 断言
    56             Tool.common_assert(resp)
    57         except Exception as e:
    58             # 日志记录
    59             log.error("断言错误:{}".format(e))
    60             # 抛异常
    61             raise

    【补充说明】

    在实现业务的测试用例时,我们还使用到了参数化以及日志记录

    与Web自动化测试,我们仍然使用data包下的.yaml文件来存储测试数据,使用tool包下的read_yaml.py模块来读取测试数据(直接复制web自动化测试项目中的read_yaml.py模块)。同时为了便于分析测试结果,我们还可以在api、scripts包下的模块中添加相应的日志记录,具体实现如下:

    • 前端登录测试数据

    • 前端发布文章测试数据

     

    • 后台管理登录测试数据

    •  测试数据读取

     1 # 用于读取yaml中的数据信息
     2 
     3 import os
     4 from config import BASE_PATH
     5 import yaml
     6 
     7 # 用于读取yaml中的数据
     8 def read_yaml(filename):
     9     file_path = BASE_PATH + os.sep + 'data' + os.sep + filename   # os.sep表示文件的路径分隔符,在windows系统中表示'\',在linux系统中表示'/'
    10     # 定义空列表,组装测试数据
    11     arr = []
    12     # 获取文件流
    13     with open(file_path,'r',encoding="utf-8") as f:     # 读取文件注意指定编码方式
    14         # 遍历 调用yaml.safe_load(f).values()
    15         for datas in yaml.safe_load(f).values():
    16             #print("hello")   # 此时循环只遍历一次
    17             arr.append(tuple(datas.values()))
    18         # 返回结果
    19         return arr
    20 
    21 if __name__ == '__main__':
    22     print(read_yaml("mt_login.yaml"))
    • 日志记录
     1 import logging
     2 import logging.handlers
     3 import os
     4 from config import BASE_PATH
     5 
     6 """
     7 确保生成的日志为同一个对象
     8 """
     9 class GetLog:
    10 
    11     # 定义类属性,保存日志
    12     __logger = None
    13 
    14     @classmethod
    15     def get_logger(cls):
    16         # 判断日志是否为空
    17         if cls.__logger is None:
    18             # 为空,则创建日志器
    19             cls.__logger = logging.getLogger('myLog_mt')
    20             # 修改默认级别 -- INFO
    21             cls.__logger.setLevel(logging.INFO)
    22             # 创建处理器 -- 将日志信息输出到指定的文件中
    23             log_path = BASE_PATH + os.sep + "log" + os.sep + "mt.log"
    24             th = logging.handlers.TimedRotatingFileHandler(filename=log_path,
    25                                                            when="midnight",
    26                                                            interval=1,
    27                                                            backupCount=3,
    28                                                            encoding="utf-8")
    29             # 创建格式器
    30             fmt = "%(asctime)s %(levelname)s [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
    31             fm = logging.Formatter(fmt)
    32             # 将格式器设置到处理器中
    33             th.setFormatter(fm)
    34             # 将处理器添加到格式器中
    35             cls.__logger.addHandler(th)
    36         # 返回日志器
    37         return cls.__logger
    38 
    39 if __name__ == '__main__':
    40     log = GetLog.get_logger()
    41     log.info("测试信息日志级别")
    42     log.error("测试错误级别")

    【Tips】

    • 在读取或存储相关数据到本项目下的文件夹中时(如读取测试数据/日志存储到指定目录下),由于由于路径中存在相同的前缀,因此我们可以将相同的前缀使用常量BASE_PATH进行统一管理,可以简化路径的书写(在项目根目录下的config.py模块中实现
    1 import os
    2 
    3 BASE_PATH = os.path.dirname(__file__)
    • 为防止使用pytest指令执行测试脚本时出现路径报错的问题,我们可以在__init__.py文件中添加如下配置信息:
    1 """
    2 用于解决路径报问题: 用于自动加载__init__.py文件所在目录下的模块
    3 """
    4 import sys
    5 import os
    6 
    7 sys.path.append(os.getcwd())

    【报错信息如下】

    七、测试脚本的执行及在线生成测试报告

    在执行测试脚本之前,我们需要通过调试确保每个测试脚本都能独立的正常运行。

    • 配置文件的编写
      • 位置:放置在项目的根目录下
      • 名称:pytest.ini
      • 内容如下:
    1 [pytest]
    2 addopts = -s --alluredir report
    3 testpaths = ./scripts
    4 python_files = test*
    5 python_classes = Test*
    6 python_functions = test*
    • 测试脚本的执行
      • 由于pytest在执行测试脚本时,是按照.py模块名称的ACSII顺序进行执行的,因此在给测试脚本命名时,处理以test开头外,可以人为的添加_a_、_b_等字母,使测试脚本按预期的顺序执行; 
      • 在Terminal中输入:pytest 
    • 在线生成测试报告
      • allure serve report

    至此,我们完成了Api自动化测试框架的搭建,当我们在搭建接口自动化测试框架时,只需要遵循如下原则即可:

    • 项目目录结构搭建  -- api、scripts、tool、data、report、log
    • api资源结构的搭建
      • 公共变量的抽取  -- 存储在api下的__init__.py模块中
      • 初始化 -- 定义接口url
      • 接口封装 -- 暂时使用pass进行占位
    • api资源结构的实现
      • 定义请求数据
      • 发送请求获取服务器返回的响应对象
    • 测试结构搭建
      • 初始化  -- 获取apiXX对象
      • 接口测试方法 -- 暂时使用pass进行占位
    • 测试结构实现
      • 调用对应api资源中的接口封装方法
      • 参数化
      • 断言
  • 相关阅读:
    Js通用验证
    C#实现马尔科夫模型例子
    C# 生成pdf文件客户端下载
    Js跨一级域名同步cookie
    C#数据库连接池 MySql SqlServer
    关于Oracle row_number() over()的简单使用
    开发中mybatis的一些常见问题记录
    Java通过图片url地址获取图片base64位字符串的两种方式
    基于apache httpclient的常用接口调用方法
    通过jcrop和canvas的画布功能完成对图片的截图功能与视频的截图功能实现
  • 原文地址:https://www.cnblogs.com/yif930916/p/15095172.html
Copyright © 2020-2023  润新知