1. docker 安装博客系统

1)先从仓库拉取镜像:

docker pull crpi-z8ulxt5cu2ysgj9k.cn-chengdu.personal.cr.aliyuncs.com/aiwear/edison_blog:V1.2

结果如下所示:

在这里插入图片描述

2)启动容器

docker container run -itd -p 9090:8080 crpi-z8ulxt5cu2ysgj9k.cn-chengdu.personal.cr.aliyuncs.com/aiwear/edison_blog:V1.2

结果如下所示:

在这里插入图片描述

3)检查容器是否启动成功

docker container ls

结果如下所示:

在这里插入图片描述

4)进入容器内部

# 通过容器ID进入到内部
docker exec -it 6ee9f635d4a2 bash

结果如下所示:

在这里插入图片描述

5)启动 MySQL 服务

# 先清理mysql残留的锁文件
rm -f /var/run/mysqld/mysqlx.sock
rm -f /var/run/mysqld/mysqld.sock
rm -f /var/run/mysqld/mysqld.sock.lock
rm -f /var/lib/mysql/*.pid

# 确保目录权限正确
chown -R mysql:mysql /var/run/mysqld
chown -R mysql:mysql /var/lib/mysql

# 最后再启动服务
service mysql start

结果如下所示:

在这里插入图片描述

另外,启动成功以后,登录 mysql 检查一下数据库的库表

# 账号root, 密码BITE@yyds.666
mysql -uroot -p

结果如下所示:

在这里插入图片描述

然后进入到 auto_test 库里面,查看是否有表

在这里插入图片描述

6)启动博客服务

# 进入后端服务目录
cd mmtest/

# 启动java服务
nohup java -jar spring-blog-auto-test.jar &

# 查看日志
tail -f nohup.out

# 查看进程
ps -ef | grep spring

结果如下所示:

在这里插入图片描述

7)访问博客系统服务

# 浏览器输入
http://IP地址:9090/blog_login.html

# 输入用户名和密码
账号: zhangsan
密码: 123456

结果如下所示:

在这里插入图片描述

2. 需求分析

理解业务需求,若是针对未参与的项目实施接口自动化,应与业务人员、产品经理等沟通,了解接口所支持的业务场景和业务逻辑。根据业务需求,明确接口需要实现的具体功能,如数据的获取、修改、删除等操作,以及接口的输入输出要求。

分析接口之间的依赖关系,确定接口的调用顺序和依赖条件。

3. 挑选接口

博客系统中接口较少,可以针对所有的接口实施自动化测试。

接口如下所示。

3.1 登录

接口地址:http://IP:9090/user/login

请求方法:POST

Content-Type:application/x-www-form-urlencoded 或 application/json

请求参数:

参数名类型必填说明
usernameString是用户名,示例:zhangsan
passwordString是密码,示例:123456

请求示例:

{
  "username": "zhangsan",
  "password": "123456"
}

返回示例:

{
    "code": "SUCCESS",
    "errMsg": "",
    "data": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MywidXNlck5hbWUiOiJ6aGFuZ3NhbiIsImV4cCI6MTc3OTU5ODkzNn0.R92wSL5FwYvGDu4JvHC0B-l-huAQLqeABKUQIeN_zIE"
}

结果如下所示:

在这里插入图片描述

3.2 获取列表页

接口地址:http://IP:9090/blog/getList

请求方法:GET

请求头:

参数名类型必填说明
user_token_headerString是登录成功后返回的 Token 凭证

请求参数:暂无

返回示例:

{
    "code": "SUCCESS",
    "errMsg": "",
    "data": [
        {
            "id": 32213,
            "title": "博客标题",
            "content": "博客内容",
            "userId": 3,
            "deleteFlag": 0,
            "createTime": "2025-03-18 11:41",
            "updateTime": "2025-03-18T03:41:34.000+00:00",
            "loginUser": false
        }
    ]
}

结果如下所示:

在这里插入图片描述

3.3 增加博客

接口地址:http://IP:9090/blog/add

请求方法:POST

请求头:

参数名类型必填说明
user_token_headerString是Token 凭证

请求参数:

{
    "title": "Apifox测试标题",
    "content": "##Apifox测试博客内容"
}

返回示例:

{
    "code": "SUCCESS",
    "errMsg": "",
    "data": true
}

结果如下所示:

在这里插入图片描述

3.4 获取用户详情

接口地址:http://IP:9090/user/getUserInfo

请求方法:GET

请求头:

参数名类型必填说明
user_token_headerString是Token 凭证

请求参数:暂无

返回示例:

{
    "code": "SUCCESS",
    "errMsg": "",
    "data": {
        "id": 3,
        "userName": "zhangsan",
        "password": "4a352aaa4c954a659f9b75ea40346edaa6226dbde8e006431d9fbdf5b6ad82a5",
        "deleteFlag": 0,
        "createTime": "2025-03-18T03:26:37.000+00:00",
        "updateTime": "2025-03-18T03:26:37.000+00:00"
    }
}

结果如下所示:

在这里插入图片描述

3.5 获取博客详情

接口地址:http://IP:9090/blog/getBlogDetail

请求方法:GET

请求头:

参数名类型必填说明
user_token_headerString是Token 凭证

请求参数:

参数名类型必填说明
blogIdString是博客ID,示例:32214

请求示例:

  • http://IP:9090/blog/getBlogDetail?blogId=32214

返回示例:

{
    "code": "SUCCESS",
    "errMsg": "",
    "data": {
        "id": 32214,
        "title": "Apifox测试标题",
        "content": "##Apifox测试博客内容",
        "userId": 3,
        "deleteFlag": 0,
        "createTime": "2026-05-25 10:05",
        "updateTime": "2026-05-25T02:05:53.000+00:00",
        "loginUser": true
    }
}

结果如下所示:

在这里插入图片描述

3.6 获取登录用户信息

接口地址:http://IP:9090/user/getAuthorInfo

请求方法:GET

请求头:

参数名类型必填说明
user_token_headerString是Token 凭证

请求参数:

参数名类型必填说明
blogIdString是博客ID,示例:32214

请求示例:

  • http://IP:9090/user/getAuthorInfo?blogId=32214

返回示例:

{
    "code": "SUCCESS",
    "errMsg": "",
    "data": {
        "id": 3,
        "userName": "zhangsan",
        "password": "4a352aaa4c954a659f9b75ea40346edaa6226dbde8e006431d9fbdf5b6ad82a5",
        "deleteFlag": 0,
        "createTime": "2025-03-18T03:26:37.000+00:00",
        "updateTime": "2025-03-18T03:26:37.000+00:00"
    }
}

结果如下所示:

在这里插入图片描述

4. 设计测试用例

针对接口设计测试用例,必须要按照完备的接口文档来进行设计,除此之外,最好也能够看到接口对应的代码,查看接口存在的不同的响应,针对不同的响应来设计测试用例。

4.1 登录接口

接口:/user/login

登录成功:正确的账号和正确的密码

登录失败:

  • 错误的账号,错误的密码
  • 错误的账号,正确的密码
  • 正确的账号,错误的密码
  • 不存在的账号
  • 账号和密码为空
  • 输入过长账号
  • 输入过长密码

不同的请求方法:

  • post
  • get

4.2 列表页接口

接口:/blog/getList

未登录状态下访问:401

登录状态下访问

不同的请求方法:

  • post
  • get

4.3 详情页接口

接口:/blog/getBlogDetail?blogId=

未登录状态下访问

登录状态下访问:

  • 正确的blogId
  • 错误的blogId:
    • blogId为空
    • blogId不存在
    • blogId为非数字:
      • 字符串
      • 浮点数
      • 特殊字符
    • blogId数字过大
    • blogId为负数

不同的请求方法:

  • post
  • get

4.4 用户认证接口

接口:/user/getAuthorInfo?blogId=

未登录状态下访问

登录状态下访问:

  • 正确的blogId
  • 错误的blogId:
    • blogId为空
    • blogId不存在
    • blogId为非数字:
      • 字符串
      • 浮点数
      • 特殊字符
    • blogId数字过大
    • blogId为负数

不同的请求方法:

  • post
  • get

4.5 编辑页接口

接口:/blog/add

未登录状态下访问:

  • 输入符合要求的标题和内容
  • 标题为空,内容不为空
  • 标题不为空,内容为空
  • 标题和内容都为空
  • 输入过长标题
  • 输入过长内容
  • 标题/内容带有特殊字符
  • 内容为图片
  • 内容为链接

登录状态下访问:

  • 输入符合要求的标题和内容
  • 标题为空,内容不为空
  • 标题不为空,内容为空
  • 标题和内容都为空
  • 输入过长标题
  • 输入过长内容
  • 标题带有特殊字符
  • 内容为图片
  • 内容为链接

不同的请求方法:

  • post
  • get

4.6 用户信息接口

接口:/user/getUserInfo

未登录状态下访问

登录状态下访问

不同的请求方法:

  • post
  • get

5. 设计自动化测试项目的架构

语言选择:python

技术栈:pytest 框架、requests 模块、PyYAML 模块、jsonschema 模块、allure-pytest 模块、logging 模块。

集成开发环境:pycharm

项目目录结构:

autotest_interface/
├── cases/                  # 所有测试脚本存放的目录--根据接口创建测试文件
│   ├── test_login.py      # 示例:登录博客的测试用例
│   ├── test_edit.py       # 示例:编辑博客的测试用例
│   └── ...
├── data/                  # 存放通用数据目录
│   ├── data.yaml          # 搜索页面类
│   └── ...
├── utils/                 # 辅助工具和库的目录
│   ├── logger_util.py     # 封装日志
│   ├── request_util.py    # 封装接口请求
│   ├── yaml_util.py       # 封装yaml操作
│   └── ...
├── allure-results/        # 测试结果输出目录
│   ├── xxxx.json
│   └── ...
├── allure-report/         # 测试报告输出目录
│   ├── index.html
│   └── ...
├── logs/                  # 测试日志目录
│   ├── 2024-12-20.log     # 所有日志
│   ├── 2024-12-20_info.log # info日志
│   └── 2024-12-20_error.log # error日志
└── pytest.ini             # 运行配置文件

创建一个 requirements.txt 文件,用于存放所有需要的依赖包:

pytest==8.3.2
allure-pytest==2.13.5
jsonschema==4.23.0
PyYAML==6.0.1
requests==2.31.0

然后在命令行执行下面的命令进行安装即可。

pip install -r requirements.txt

6. 封装工具类 / 方法

6.1 封装日志

代码如下所示:

import logging
import os.path
import time

class infoFilter(logging.Filter):
    def filter(self, record):
        return record.levelno == logging.INFO

class errFilter(logging.Filter):
    def filter(self, record):
        return record.levelno == logging.ERROR

class logger:

    #获取日志对象---定义类方法@classmethod
    @classmethod
    def getlog(cls):

        #创建日志对象
        cls.logger = logging.getLogger(__name__)
        cls.logger.setLevel(logging.DEBUG)

        #保证logs文件夹必须创建好了
        LOG_PATH = "./logs/"
        if not os.path.exists(LOG_PATH):
            os.mkdir(LOG_PATH)

        #将日志输出到日志文件中
        '''
        logs
            2025-12-12.log
            2025-12-12-info.log
            2025-12-12-err.log
        '''
        now = time.strftime("%Y-%m-%d")
        log_name = LOG_PATH + now + ".log"
        info_log_name = LOG_PATH + now + "-info.log"
        err_log_name = LOG_PATH + now + "-err.log"


        #创建文件处理器
        all_handler = logging.FileHandler(log_name,encoding="utf-8")
        info_handler = logging.FileHandler(info_log_name,encoding="utf-8")
        err_handler = logging.FileHandler(err_log_name,encoding="utf-8")

        #创建处理器,将日志输出到控制台
        streamHandler = logging.StreamHandler()


        #设置日志的格式
        formatter = logging.Formatter(
            "%(asctime)s %(levelname)s [%(name)s] [%(filename)s (%(funcName)s:%(lineno)d)] - %(message)s"
        )

        all_handler.setFormatter(formatter)
        info_handler.setFormatter(formatter)
        err_handler.setFormatter(formatter)
        streamHandler.setFormatter(formatter)

        #添加过滤器
        info_handler.addFilter(infoFilter())
        err_handler.addFilter(errFilter())

        cls.logger.addHandler(all_handler)
        cls.logger.addHandler(info_handler)
        cls.logger.addHandler(err_handler)
        # cls.logger.addHandler(streamHandler)

        return cls.logger

6.2 封装请求

代码如下所示:


6.3 封装 yaml 读取

代码如下所示:

'''
yaml相关的操作
'''
import os

import yaml


#往yaml文件中写入数据
def write_yaml(filename,data):
    with open(os.getcwd()+"/data/"+filename,mode="a+",encoding="utf-8") as f:
        yaml.safe_dump(data,stream=f)

#读取yaml文件中的数据
def read_yaml(filename,key):
    with open(os.getcwd()+"/data/"+filename,mode="r",encoding="utf-8") as f:
        data = yaml.safe_load(f)
        return data[key]

#清空
def clear_yaml(filename):
    with open(os.getcwd()+"/data/"+filename,mode="w",encoding="utf-8") as f:
        f.truncate()

7. 测试用例编写

7.1 登录页 - 用例编写

test_login.py 代码如下所示:

'''
登录——接口自动化测试
url:http://8.156.77.78:9090/user/login  POST
form-data {"username":"zhangsan","password":"123456"}
'''
import re

import pytest

from jsonschema import validate
from utils.request_util import host, Request
from utils.yaml_util import write_yaml

#@pytest.mark.order(1)
class TestLogin:
    url = host + "user/login"
    schema = {
      "type": "object",
      "required": ["code","errMsg","data"],
      "additionalProperties": False,
      "properties": {
        "code": {
          "type": "string"
        },
        "errMsg": {
          "type": "string"
        },
        "data": {
          "type": ["string","null"]
        }
      }
    }

    @pytest.mark.parametrize("login",[
        #错误的账号和密码
        {
            "username": "zhang",
            "password": "123",
            "errMsg":"用户不存在"
        },
        #错误的账号,正确的密码
        {
            "username": "zhang",
            "password": "123456",
            "errMsg": "用户不存在"
        },
        #正确的账号,错误的密码
        {
            "username": "zhangsan",
            "password": "123",
            "errMsg":"密码错误"
        },
        #不存在的账号
        {
            "username": "edisontest",
            "password": "xxxxxx",
            "errMsg": "用户不存在"
        },
        #账号和密码都为空
        {
            "username": "",
            "password": "",
            "errMsg": "账号或密码不能为空"
        },
        #过长的账号
        {
            "username": "这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号这是一个很长的账号",
            "password": "123456",
            "errMsg": "用户不存在"
        },
        #过长的密码
        {
            "username": "zhangsan",
            "password": "这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码这是一个很长的密码",
            "errMsg": "密码错误"
        }
    ])
    #异常登录——放在正常登录之前
    def test_login_fail(self,login):
        data = {
            "username": login["username"],
            "password": login["password"]
        }
        r = Request().post(url=self.url, data=data)

        validate(instance=r.json(), schema=self.schema)

        assert r.json()["code"] == "FAIL"
        assert r.json()['errMsg'] == login["errMsg"]


    #正常登录
    @pytest.mark.parametrize("login",[
        {
            "username":"zhangsan",
            "password":"123456",
        },
        {
            "username": "lisi",
            "password": "123456",
        }
    ])
    def test_login_success(self,login):
        data = {
            "username": login["username"],
            "password": login["password"]
        }
        r = Request().post(url=self.url,data=data)

        validate(instance=r.json(),schema=self.schema)

        assert r.json()["code"] == "SUCCESS"
        assert re.match(r'\S{100,}',r.json()['data'])

        #接口返回的data就是用户的登录凭证----作为其他接口的登录凭证

        token = {
            "user_token_header":r.json()['data']
        }
        write_yaml("data.yml",token)

测试结果如下所示:

在这里插入图片描述

7.2 列表页 - 用例编写

代码如下所示:

import pytest
from jsonschema import validate
from utils.request_util import host, Request
from utils.yaml_util import read_yaml, write_yaml

#@pytest.mark.order(2)
class TestList:
    url = host + "blog/getList"
    schema = {
      "type": "object",
      "required": ["code","errMsg","data"],
        "additionalProperties": False,
        "properties": {
        "code": {
          "type": "string"
        },
        "errMsg": {
          "type": "string"
        },
        "data": {
          "type": "array",
          #要求返回的博客数据最少要有一个
          "minItems": 1,
          "items": {
            "type": "object",
            "required": ["id","title","content","userId","deleteFlag","createTime","updateTime","loginUser"],
            "additionalProperties": False,
              "properties": {
              "id": {
                "type": "number"
              },
              "title": {
                "type": "string"
              },
              "content": {
                "type": "string"
              },
              "userId": {
                "type": "number"
              },
              "deleteFlag": {
                "type": "number"
              },
              "createTime": {
                "type": "string"
              },
              "updateTime": {
                "type": "string"
              },
              "loginUser": {
                "type": "boolean"
              }
            }
          }
        }
      }
    }

    #未登录的状态下请求列表页--401
    def test_list_noLogin(self):
        r = Request().get(url=self.url)
        assert r.status_code == 401

    #请求列表页——登录的场景下
    def test_list_login(self):
        token = read_yaml("data.yml","user_token_header")
        header = {
            "user_token_header":token
        }
        r = Request().get(url=self.url,headers = header)
        #jsonSchema校验
        validate(instance=r.json(),schema=self.schema)
        #关键字段值的校验
        assert r.json()['code'] == "SUCCESS"

        #提取有效的blogid存储在yaml文件中
        blogId = {
            "blogId":r.json()['data'][0]["id"]
        }
        write_yaml("data.yml",blogId)

测试结果如下所示:

在这里插入图片描述

7.3 详情页 - 用例编写

代码如下所示:

import pytest
from jsonschema import validate
from utils.request_util import host, Request
from utils.yaml_util import read_yaml


class TestDetail:
    url = host +"blog/getBlogDetail"
    schema = {
      "type": "object",
      "required": ["code","errMsg","data"],
        "additionalProperties": False,
        "properties": {
        "code": {
          "type": "string"
        },
        "errMsg": {
          "type": "string"
        },
        "data": {
          "type": "object",
          "additionalProperties": False,
          "required": ["id","title","content","userId","deleteFlag","createTime","updateTime","loginUser"],
          "properties": {
            "id": {
              "type": "number"
            },
            "title": {
              "type": "string"
            },
            "content": {
              "type": "string"
            },
            "userId": {
              "type": "number"
            },
            "deleteFlag": {
              "type": "number"
            },
            "createTime": {
              "type": "string"
            },
            "updateTime": {
              "type": "string"
            },
            "loginUser": {
              "type": "boolean"
            }
          }
        }
      }
    }

    # 未登录状态下访问博客详情页
    def test_detail_noLogin(self):
        url = self.url + "?blogId=1234"
        r = Request().get(url=url)
        assert r.status_code == 401

    # 登录状态下请求博客详情页
    # 正常请求
    def test_detail_login(self):
        url = self.url + "?blogId=" + str(read_yaml("data.yml", "blogId"))
        token = read_yaml("data.yml", "user_token_header")
        header = {
            "user_token_header": token
        }
        r = Request().get(url=url, headers=header)

        # jsonSchema校验
        validate(instance=r.json(), schema=self.schema)
        # assert关键字段值的校验
        assert r.json()["code"] == "SUCCESS"

    # 博客详情页——blogId错误
    @pytest.mark.parametrize("blogId", ["", 1234, "edison", -100, 999999999999999999999999999999999999])
    def test_detail_fail(self, blogId):
        url = self.url
        # 配置参数
        params = {
            "blogId": blogId
        }

        token = read_yaml("data.yml", "user_token_header")
        header = {
            "user_token_header": token
        }
        r = Request().get(url=url, headers=header)

        expect_json = {
            "code": "FAIL",
            "errMsg": "内部错误, 请联系管理员",
            "data": None
        }

        assert r.json() == expect_json

结果如下所示:

在这里插入图片描述

7.4 添加博客 - 用例编写

代码如下所示:

'''
添加博客接口
'''
from pickle import FALSE

import pytest
from jsonschema import validate

from utils.request_util import host, Request
from utils.yaml_util import read_yaml


class TestAdd:
    url = host + "blog/add"
    schema = {
      "type": "object",
      "required": ["code","errMsg","data"],
      "additionalProperties": False,
      "properties": {
        "code": {
          "type": "string"
        },
        "errMsg": {
          "type": "string"
        },
        "data": {
          "type": "boolean"
        }
      }
    }

    #未登录状态下请求add接口
    def test_add_noLogin(self):
        r = Request().post(url=self.url)
        r.status_code == 401

    #测试添加博客——添加成功+添加失败
    @pytest.mark.parametrize("add",[
        #添加成功
        {
            "title": "接口自动化标题",
            "content": "接口自动化内容",
            "data":True
        },
        #标题为空
        {
            "title": "",
            "content": "接口自动化内容",
            "data": False
        },
        #内容为空
        {
            "title": "接口自动化标题",
            "content": "",
            "data": False
        },
        #标题和内容都为空
        {
            "title": "",
            "content": "",
            "data": False
        },
        #添加带有图片的博客
        {
            "title": "接口自动化标题--带有图片",
            "content": "![](https://pic.rmb.bdstatic.com/bjh/down/gxGspORnpcqm_GBISeO0iQ15d06ce241eb6b8aa7da1dcf17317911.jpg?for=bg)",
            "data": True
        },
        #添加带有链接的博客
        {
            "title": "接口自动化标题--带有链接",
            "content": "[百度首页](http://www.baidu.com \"百度首页\")",
            "data": True
        },
    ])
    def test_add(self,add):

        token = read_yaml("data.yml","user_token_header")
        header = {
            "user_token_header":token
        }

        json = {
            "title": add["title"],
            "content": add["content"]
        }

        r = Request().post(url=self.url,json = json,headers = header)
        #验证jsonSchema
        validate(instance=r.json(),schema=self.schema)

        #assert关键字段的值要匹配
        assert r.json()['data'] == add["data"]

结果如下所示:

在这里插入图片描述

7.5 用户登录信息 - 用例编写

代码如下所示:

import pytest
from jsonschema import validate
from utils.request_util import host, Request
from utils.yaml_util import read_yaml


class TestgetAuthorInfo:
    url = host + "user/getAuthorInfo"
    schema = {
      "type": "object",
      "required": ["code","errMsg","data"],
        "additionalProperties": False,
        "properties": {
        "code": {
          "type": "string"
        },
        "errMsg": {
          "type": "string"
        },
        "data": {
          "type": ["object","null"],
          "additionalProperties": False,
          "required": ["id","userName","password","githubUrl","deleteFlag","createTime","updateTime"],
          "properties": {
            "id": {
              "type": "number"
            },
            "userName": {
              "type": "string"
            },
            "password": {
              "type": "string"
            },
            "githubUrl": {
              "type": "string"
            },
            "deleteFlag": {
              "type": "number"
            },
            "createTime": {
              "type": "string"
            },
            "updateTime": {
              "type": "string"
            }
          }
        }
      }
    }

    #未登录状态访问接口
    def test_getAuthorInfo_noLogin(self):
        url = self.url + "?blogId=32214"
        r = Request().get(url=url)
        assert r.status_code == 401

    #登录状态下正确请求
    #有效的blogId
    def test_getAuthorInfo(self):
        blogId = read_yaml("data.yml","blogId")
        url = self.url + "?blogId=" + str(blogId)
        #读取用户登录凭证
        token = read_yaml("data.yml","user_token_header")
        header = {
            "user_token_header" : token
        }
        #发起请求
        r = Request().get(url = url,headers = header)

        #校验jsonschema
        validate(instance=r.json(),schema=self.schema)
        #asser校验关键数据
        assert r.json()['code'] == "SUCCESS"

    #登录状态下异常请求
    #blogId异常
    @pytest.mark.parametrize("blogId,expected_code",[
        ("","FAIL"),
        (1234,"FAIL"),
        (-100,"SUCCESS"),
        ("爱迪生","FAIL"),
        (999999999999999999999999999999999999,"FAIL")
    ])
    def test_getAuthorInfo_fail(self,blogId,expected_code):
        url = self.url
        # 配置参数
        params = {
            "blogId": blogId
        }
        # 读取用户登录凭证
        token = read_yaml("data.yml", "user_token_header")
        header = {
            "user_token_header": token
        }
        # 发起请求
        r = Request().get(url=url, headers=header,params = params)

        validate(instance=r.json(),schema=self.schema)
        assert r.json()["code"] == expected_code

结果如下所示:

在这里插入图片描述

7.6 用户信息 - 用例编写

代码如下所示:

from jsonschema import validate
from utils.request_util import host, Request
from utils.yaml_util import read_yaml


class TestgetUserInfo:
    url = host + "user/getUserInfo"
    schema = {
      "type": "object",
      "required": ["code","errMsg","data"],
       "additionalProperties": False,
        "properties": {
        "code": {
          "type": "string"
        },
        "errMsg": {
          "type": "string"
        },
        "data": {
          "type": "object",
          "additionalProperties": False,
          "required": ["id","userName","password","githubUrl","deleteFlag","createTime","updateTime"],
          "properties": {
            "id": {
              "type": "number"
            },
            "userName": {
              "type": "string"
            },
            "password": {
              "type": "string"
            },
            "githubUrl": {
              "type": "string"
            },
            "deleteFlag": {
              "type": "number"
            },
            "createTime": {
              "type": "string"
            },
            "updateTime": {
              "type": "string"
            }
          }
        }
      }
    }

    #未登陆状态下请求接口
    def test_getUserInfo_noLogin(self):
        r = Request().get(url=self.url)
        assert r.status_code  == 401

    #登陆状态下请求接口
    def test_getUserInfo(self):
        #添加请求头
        token = read_yaml("data.yml","user_token_header")
        header = {
            "user_token_header":token
        }
        r = Request().get(url=self.url,headers = header)
        #校验jsonschema
        validate(instance=r.json(),schema=self.schema)
        #assser校验关键数据
        assert r.json()["code"] == "SUCCESS"


结果如下所示:

在这里插入图片描述

8. 执行测试用例

在使用 pytest 进行测试时,有时候我们需要按照特定的顺序来运行测试用例,尤其是在涉及到测试用例之间的依赖关系时。pytest 本身并不直接支持通过配置来改变测试用例的默认运行顺序,pytest-order 是一个第三方插件,专门用于控制测试用例的执行顺序。

首先,你需要安装这个插件:

pip install pytest-order==1.3.0

结果如下所示:

在这里插入图片描述

既可以用在测试类上,也可以用在测试方法上,以测试类为例:

@pytest.mark.order(1)
class TestLogin:

@pytest.mark.order(2)
class TestList:

这里必须优先执行【登录接口测试用例】和【列表页接口测试用例】。

在这里插入图片描述

9. 生成测试报告并分析结果

在 pytest.ini 配置文件中添加 pytest 参数:

[pytest]
addopts = -vs --alluredir allure-results

然后在命令行输入 pytest 命令,即可生成测试报告的 json 文件:

在这里插入图片描述

然后在命令输入 allure generate .\allure-results\ -o .\allure-reports\ 来生成本地的测试报告

在这里插入图片描述

然后打开 index.html 网页即可。

在这里插入图片描述

测试时间:测试执行时间从 19:15:56 持续到 19:15:58,总共耗时 1 秒 357 毫秒。测试时间与测试用例数量成正比。用例数量越多,测试时间越长。通过优化测试脚本、并行执行和分布式测试环境,可以显著缩短测试时间。

测试用例总数:共有 34 个测试用例,高测试用例总数通常表示测试覆盖范围广,能够更全面地验证接口功能;低测试用例总数可能意味着测试覆盖不全面,存在遗漏的风险。

通过率:饼图显示了测试的通过率为 100%,这意味着所有 30 多个测试用例都成功执行,没有失败的测试用例。通过率是衡量接口质量和测试效果的关键指标。在测试环境中,通过率应达到 95% 以上。

更多推荐