Skip to content

接口自动化测试实战

接口自动化测试实战

学习体系

  • 掌握接口自动化测试中的各种格式的请求构造与响应断言技巧。

知识模块

  • 接口自动化测试 L2

实战需求

实战 1:LiteMall 后台管理系统登录接口测试

  • LiteMall 后台管理系统:https://litemall.hogwarts.ceshiren.com
    • 登录接口
  • 使用浏览器开发者工具抓包获取接口信息
  • 对上述接口完成接口自动化冒烟测试
  • 添加 allure 描述并生成 allure 报告

实战 2:LiteMall 后台管理系统商品管理接口测试

  • LiteMall 后台管理系统:https://litemall.hogwarts.ceshiren.com
    • 上架商品
    • 查询商品
    • 更新商品
    • 删除商品
  • 使用浏览器开发者工具抓包获取接口信息
  • 对上述接口完成接口自动化冒烟测试
  • 添加 allure 描述并生成 allure 报告

相关知识点

教程地址 教程视频地址 时间位置
代理配置 代理配置 全部 12:38
多层嵌套响应断言 多层嵌套响应断言 全部 31:02

实战演练

实战 1:LiteMall 后台管理系统登录接口测试

@allure.feature("Litemall电子商城")
class TestLitemall:

    @allure.story("登录")
    @allure.title("登录接口-冒烟用例")
    def test_login(self):
        '''
        litemall 登录接口
        '''
        # 登录,获取 token
        self.base_url = 'https://litemall.hogwarts.ceshiren.com'
        # 登录 url
        url = f'{self.base_url}/admin/auth/login'
        # 登录帐号信息
        data = {
            'username': 'hogwarts',
            'password': 'test12345'
        }
        # 发出登录请求
        r = requests.post(url, json=data, verify=False)
        # 打印登录接口响应体
        print(r.text)
        # 提取token
        self.token = r.json().get("data").get("token")
        # 断言响应状态码
        assert r.status_code == 200
        # 断言业务状态
        assert r.json().get("errno") == 0

实战 2:LiteMall 后台管理系统商品管理接口测试

import random
import allure
import jsonpath
import requests

@allure.feature("Litemall电子商城")
class TestLitemall:

    # 测试前置装置,测试类执行之前只执行一次
    def setup_class(self):
        # 登录,获取 token
        self.base_url = 'https://litemall.hogwarts.ceshiren.com'
        # 登录 url
        url = f'{self.base_url}/admin/auth/login'
        # 登录帐号信息
        data = {
            'username': 'hogwarts',
            'password': 'test12345'
        }
        # 发出登录请求
        r = requests.post(url, json=data, verify=False)
        # 打印登录接口响应体
        print(r.text)
        # 提取token
        self.token = r.json().get("data").get("token")
        # 准备测试数据
        # 商品编号
        self.good_sn = random.randint(1000000000, 10000000000)
        print(f"生成的商品编号为 {self.good_sn}")
        # 添加商品详细信息
        self.data = {
            "goods": {
                "goodsSn": self.good_sn,
                "name": f"xx2-{random.randint(1, 1000000)}"
            },
            "specifications": [
                {
                    "specification": "规格",
                    "value": "标准"
                }
            ],
            "products": [
                {
                    "specifications": [
                        "标准"
                    ],
                    "price": 0,
                    "number": 0,
                }
            ],
            "attributes": [
                {
                    "attribute": "材质",
                    "value": "纯棉"
                }
            ]
        }
        # 拼接头信息,完成业务接口鉴权
        self.headers = {
            'X-Litemall-Admin-Token': self.token
        }

    @allure.story("上架商品")
    @allure.title("上架商品-冒烟用例")
    def test_add(self):
        '''
        测试上架商品
        '''
        # 商品上架接口 url
        url = f'{self.base_url}/admin/goods/create'
        # 发出上架商品请求(post 请求,携带 json 格式请求体)
        r = requests.post(url, json=self.data, headers=self.headers, verify=False)
        print(f"添加接口响应为 {r.text}")
        # 获取 json 格式响应
        r_data = r.json()
        # 断言响应状态码符合预期
        assert r.status_code == 200
        # 断言响应体中 errmsg 字段符合预期
        assert r_data.get("errmsg") == "成功"
        # 断言响应体中 error 字段符合预期
        assert r_data.get("errno") == 0

    def get_good_by_goodSn(self):
        '''
        通过查询商品编号获取商品信息
        :return: 返回响应对象与商品 id
        '''
        # 拼接查询接口 url
        url = f'{self.base_url}/admin/goods/list'
        # 通过商品编号查询商品详细信息
        params = {
            'goodsSn': self.good_sn
        }
        # 发出查询请求
        r = requests.get(url, params=params, headers=self.headers, verify=False)
        print(f"查询接口响应为 {r.text}")
        # 提出响应中查询到的第一个商品的 id
        r_data = r.json()
        good_id = r_data.get("data").get("list")[0].get("id")
        print(f"商品 id 为 {good_id}")
        # 返回响应对象与商品 id
        return r, good_id

    @allure.story("查询商品")
    @allure.title("查询商品-冒烟用例")
    def test_get(self):
        '''
        测试查询接口
        '''
        # 调用查询接口,获取对应商品编号的商品信息
        r, good_id = self.get_good_by_goodSn()
        r_data = r.json()
        # 断言
        assert r.status_code == 200
        # 断言响应体中 errmsg 字段符合预期
        assert r_data.get("errmsg") == "成功"
        # 断言响应体中 error 字段符合预期
        assert r_data.get("errno") == 0
        # 使用 jsonpath 断言
        goods_sn = jsonpath.jsonpath(r.json(), "$..goodsSn")
        print(f"goodsSn={goods_sn[0]}")
        assert goods_sn[0] == f"{self.good_sn}"

    @allure.story("更新商品")
    @allure.title("更新商品-冒烟用例")
    def test_update(self):
        '''
        测试更新接口
        '''
        # 调用查询接口,获取对应商品编号的商品信息
        r_qurey, good_id = self.get_good_by_goodSn()
        url = f"{self.base_url}/admin/goods/update"
        # 更新商品信息
        self.good_update = {
          "attributes": [
            {
              "addTime": "2023-07-16 09:24:27",
              "attribute": "材质",
              "deleted": False,
              "goodsId": good_id,
              "id": 1460,
              "updateTime": "2023-07-16 09:24:27",
              "value": "纯棉"
            }
          ],
          "goods": {
            "addTime": "2023-07-16 09:24:27",
            "brandId": None,
            "brief": "",
            "categoryId": 0,
            "counterPrice": 0,
            "deleted": False,
            "goodsSn": self.good_sn,
            "id": good_id,
            "isHot": False,
            "isNew": True,
            "isOnSale": True,
            "keywords": None,
            "name": "update",
            "picUrl": "string",
            "retailPrice": 0,
            "shareUrl": "",
            "sortOrder": 100,
            "unit": "’件‘",
            "updateTime": "2023-07-16 09:24:27"
          },
          "products": [
            {
              "addTime": "2023-07-16 09:24:27",
              "deleted": False,
              "goodsId": good_id,
              "id": 258699,
              "number": 0,
              "price": 0,
              "specifications": [
                "标准"
              ],
              "updateTime": "2023-07-16 09:24:27",
            }
          ],
          "specifications": [
            {
              "addTime": "2023-07-16 09:24:27",
              "deleted": False,
              "goodsId": good_id,
              "id": 258712,
              "picUrl": "",
              "specification": "规格",
              "updateTime": "2023-07-16 09:24:27",
              "value": "标准"
            }
          ]
        }
        # 发出更新请求
        r = requests.post(url, json=self.good_update, headers=self.headers, verify=False)
        print(f"更新接口响应为 {r.text}")
        r_data = r.json()
        assert r.status_code == 200
        # 断言响应体中 errmsg 字段符合预期
        assert r_data.get("errmsg") == "成功"
        # 断言响应体中 error 字段符合预期
        assert r_data.get("errno") == 0

    @allure.story("删除商品")
    @allure.title("删除商品-冒烟用例")
    def test_delete(self):
        # 调用查询接口,获取对应商品编号的商品信息
        r_query, good_id = self.get_good_by_goodSn()
        # 通过商品 id 删除商品
        url = f'{self.base_url}/admin/goods/delete'
        data = {
            "id": good_id,
        }
        # 发出删除请求
        r = requests.post(url, json=data, headers=self.headers, verify=False)
        print(f"删除接口响应为 {r.text}")
        r_data = r.json()
        assert r.status_code == 200
        # 断言响应体中 errmsg 字段符合预期
        assert r_data.get("errmsg") == "成功"
        # 断言响应体中 error 字段符合预期
        assert r_data.get("errno") == 0

总结

  • 掌握接口自动化测试中的各种格式的请求构造与响应断言技巧。