Skip to content

【实战】宠物商店接口自动化测试实战

宠物商店接口自动化测试实战

被测产品

  • PetStore 宠物商城:
    • 一个在线的小型的商城。
    • 主要提供了增删查改等操作接口。
    • 结合 Swagger 实现了接口的管理。

需求说明

  • 完成宠物商城宠物查询功能接口自动化测试。
    • 编写自动化测试脚本。
    • 完成断言。

相关知识点

形式 章节 描述
知识点 接口请求方法 http 接口请求方法构造
知识点 接口请求参数 http 接口请求参数构造
知识点 接口请求体-json http 接口请求体为 json 格式
知识点 接口响应断言 http 接口响应状态码断言

实战思路

uml diagram

宠物商店需求分析

  • 被测产品:宠物商店系统 - 查询宠物信息
  • 宠物商店接口文档:https://petstore.swagger.io/

接口测试用例设计

  • 宠物查询单接口用例

编写接口自动化测试脚本思路

  1. 查询宠物信息。

uml diagram

编写自动化测试脚本

class TestPetstorePetsearch:

    def setup_class(self):
        # 定义接口请求 URL
        self.base_url = "https://petstore.swagger.io/v2/pet"
        self.search_url = self.base_url + "/findByStatus"

    def test_search_pet(self):
        # 查询接口请求参数
        params = {
            "status": "available"
        }
        # 发出查询请求
        r = requests.get(self.search_url, params=params)
        # 状态断言
        assert r.status_code == 200
        # 业务断言
        assert r.json() != []

脚本优化 - 添加日志

  • 新建日志配置。
  • 在用例中使用配置好的日志实例。

创建 utils 包,包下创建 log_util.py 文件。

# 配置日志
import logging
import os

from logging.handlers import RotatingFileHandler

# 绑定绑定句柄到logger对象
logger = logging.getLogger(__name__)
# 获取当前工具文件所在的路径
root_path = os.path.dirname(os.path.abspath(__file__))
# 拼接当前要输出日志的路径
log_dir_path = os.sep.join([root_path, '..', f'/logs'])
if not os.path.isdir(log_dir_path):
    os.mkdir(log_dir_path)
# 创建日志记录器,指明日志保存路径,每个日志的大小,保存日志的上限
file_log_handler = RotatingFileHandler(os.sep.join([log_dir_path, 'log.log']), maxBytes=1024 * 1024, backupCount=10)
# 设置日志的格式
date_string = '%Y-%m-%d %H:%M:%S'
formatter = logging.Formatter(
    '[%(asctime)s] [%(levelname)s] [%(filename)s]/[line: %(lineno)d]/[%(funcName)s] %(message)s ', date_string)
# 日志输出到控制台的句柄
stream_handler = logging.StreamHandler()
# 将日志记录器指定日志的格式
file_log_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)
# 为全局的日志工具对象添加日志记录器
# 绑定绑定句柄到logger对象
logger.addHandler(stream_handler)
logger.addHandler(file_log_handler)
# 设置日志输出级别
logger.setLevel(level=logging.INFO)

然后在脚本中把 print 替换为 logger.info

class TestPetstorePetsearch:

    def setup_class(self):
        # 定义接口请求 URL
        self.base_url = "https://petstore.swagger.io/v2/pet"
        self.search_url = self.base_url + "/findByStatus"

    def test_search_pet(self):
        # 查询接口请求参数
        params = {
            "status": "available"
        }
        # 发出查询请求
        r = requests.get(self.search_url, params=params)
        # 查看接口响应
        logger.info(r.text)

添加断言

class TestPetstorePetsearch:

    def setup_class(self):
        # 定义接口请求 URL
        self.base_url = "https://petstore.swagger.io/v2/pet"
        self.search_url = self.base_url + "/findByStatus"

    def test_search_pet(self):
        # 查询接口请求参数
        params = {
            "status": "available"
        }
        # 发出查询请求
        r = requests.get(self.search_url, params=params)
        # 查看接口响应
        logger.info(r.text)
        # 状态断言
        assert r.status_code == 200
        # 业务断言
        assert r.json() != []
        # 响应返回的是一个列表,判断列表中第一个元素中是否包含 id 这个 key
        assert "id" in r.json()[0]

脚本优化 - 参数化

  • 使用 pytest parametrize 装饰器实现宠物状态的参数化。
class TestPetstorePetsearch:

    def setup_class(self):
        # 定义接口请求 URL
        self.base_url = "https://petstore.swagger.io/v2/pet"
        self.search_url = self.base_url + "/findByStatus"

    @pytest.mark.parametrize("status",
            ["available", "pending", "sold"],
            ids=["available_pets", "pending_pets", "sold_pets"]
    )
    def test_search_pet_by_param(self, status):
        # 查询接口请求参数
        parmas = {
            "status": status
        }
        # 添加日志
        logger.info(f"查询状态为 {status} 的宠物信息")
        # 发出查询请求
        r = requests.get(self.search_url, params=parmas)
        logger.info(f"查询接口响应为:{r.text}")
        # 状态断言
        assert r.status_code == 200
        # 业务断言
        assert r.json() != []
        assert "id" in r.json()[0]
异常场景覆盖
class TestPetstorePetsearch:

    def setup_class(self):
        # 定义接口请求 URL
        self.base_url = "https://petstore.swagger.io/v2/pet"
        self.search_url = self.base_url + "/findByStatus"

    @pytest.mark.parametrize("status",
            ["petstatus", "", 12345,],
            ids=["wrong_value", "none_str", "number"]
    )
    def test_search_pet_failure(self, status):
        # 查询接口请求参数
        parmas = {
            "status": status
        }
        # 添加日志
        logger.info(f"查询状态为 {status} 的宠物信息")
        # 发出查询请求
        r = requests.get(self.search_url, params=parmas)
        logger.info(f"查询接口响应为:{r.text}")
        # 状态断言
        assert r.status_code == 200
        # 业务断言
        assert r.json() == []

    def test_search_pet_none_param(self):
        # 发出查询请求
        r = requests.get(self.search_url)
        logger.info(f"查询接口响应为:{r.text}")
        # 状态断言
        assert r.status_code == 200
        # 业务断言
        assert r.json() == []

    def test_search_pet_wrong_param(self):
        # 查询接口请求参数
        parmas = {
            "key": "available"
        }
        # 发出查询请求
        r = requests.get(self.search_url, params=parmas)
        logger.info(f"查询接口响应为:{r.text}")
        # 状态断言
        assert r.status_code == 200
        # 业务断言
        assert r.json() == []    

生成测试报告

  • 安装 allure 相关依赖。

给测试脚本填写一些描述信息。

@allure.feature("宠物搜索接口")
class TestPetstorePetsearch:

    def setup_class(self):
        # 定义接口请求 URL
        self.base_url = "https://petstore.swagger.io/v2/pet"
        self.search_url = self.base_url + "/findByStatus"

    @pytest.mark.parametrize("status",
            ["available", "pending", "sold"],
            ids=["available_pets", "pending_pets", "sold_pets"]
    )
    @allure.story("冒烟用例")
    def test_search_pet_by_param(self, status):
        # 查询接口请求参数

    @pytest.mark.parametrize("status",
            ["petstatus", "", 12345,],
            ids=["wrong_value", "none_str", "number"]
    )
    @allure.story("status 传入错误的值")
    def test_search_pet_failure(self, status):
        # 查询接口请求参数

    @allure.story("不传 status 参数")
    def test_search_pet_none_param(self):
        # 发出查询请求

    @allure.story("传入非 status 参数")
    def test_search_pet_wrong_param(self):
        # 查询接口请求参数

添加好之后,执行这个测试脚本。

# 生成报告信息
pytest -vs test_petclinic_petsearch.py --alluredir=./report --clean-alluredir
# 生成报告在线服务,查看报告
allure serve ./report/

总结

  • 通过 Swagger 文档获取接口信息。
  • 使用 Requests 发出携带请求参数的 GET 请求。
  • 断言响应符合是否符合预期。
  • 添加 Log 日志。
  • 使用参数化方式实现一条用例可执行多个测试数据。
  • 生成 Allure 测试报告。