【博学谷学习记录】超强总结,用心分享|软件测试第五阶段接口IHRM测试项目总结

接口测试小项目

这周我完成了IHRM人力资源管理项目其中的登录接口员工管理的测试用例的编写, 使用Python, Request和Unittest框架完成了接口自动化测试,生成测试报告。最后,使用Jenkins进行了可持续集成测试。 不足之处希望大佬指正!

1. 说明

1.1 项目介绍

IHRM人力资源管理系统包含组织管理, 员工管理, 招聘管理,劳动合同等等功能模块。 技术栈包含
前端:

  • 以Node.js为核心的Vue.js前端技术生态架构

后端:

  • SpringBoot+SpringCloud+SpringMVC+SpringData (Spring全家桶)
  • MySQL + Redis + RabbitMQ

其中完整的pi文档链接:接口文档

1.2 项目架构

在这里插入图片描述
架构说明

  • 因为我没有写从调用数据库的工具类在这个小项目里。所以TestCase和数据库交互的部分我打了叉。
  • 核心在于将测试用例与被测试系统API进行分离,便于后期维护
  • 测试用例是通过unittest进行管理,并提供了丰富的断言(等于、包含等)
  • 可以通过参数化思想测试数据与测试脚本的分离
  • 借助第三方工具快速的生成HTML报告

1.3 项目目录结构

这个小项目链接: interface_project2
项目目录结构如下所示

  • interface_project2 – 项目代号
    • data – 管理测试数据的文件夹
    • report – 管理测试结果报告的文件夹
    • api – 封装被测试系统的接口
    • scripts – 测试用例脚本
    • tools – 第三方工具包管理
    • app.py – 配置信息文件
    • run_suite.py – 测试用例执行入口
    • utils.py – 自定义工具类

1.4 IHRM项目API文档解析

IHRM项目的部分API文档解析如下图所示:
在这里插入图片描述

2. 代码实现

2.1 封装被测试系统的接口

登录接口login.py

# 导包
import requests

"""
被测系统的接口封装
登录: "http://ihrm2-test.itheima.net/api/sys/login"
"""


# 定义接口类
class LoginAPI:
    # 初始化
    def __init__(self):
        self.login_url = "http://ihrm2-test.itheima.net/api/sys/login"

    # 登录接口
    def login(self, login_data):
        # 这里不是表单而是json
        return requests.post(url=self.login_url, json=login_data)


if __name__ == '__main__':
    response = LoginAPI().login({"mobile": "13800000002", "password": "123456"})
    print(response.json())

员工管理接口employee.py

# 导包
import app
import requests


# 创建接口类
class EmployeeAPI:
    # 初始化
    def __init__(self):
        self.url_add_employee = app.BASE_URL + "/api/sys/user"
        self.url_update_employee = app.BASE_URL + "/api/sys/user/{}"
        self.url_get_employee = app.BASE_URL + "/api/sys/user/{}"
        self.url_delete_employee = app.BASE_URL + "/api/sys/user/{}"

    # 员工添加
    def add_employee(self, add_employee_data):
        return requests.post(url=self.url_add_employee, json=add_employee_data, headers=app.headers_data)

    # 员工修改
    def update_employee(self, employee_id, update_data):
        url = self.url_update_employee.format(employee_id)
        return requests.put(url=url, json=update_data, headers=app.headers_data)

    # 员工查询
    def get_employee(self, employee_id):
        url = self.url_update_employee.format(employee_id)
        return requests.get(url=url, headers=app.headers_data)

    # 员工删除
    def delete_employee(self, employee_id):
        url = self.url_update_employee.format(employee_id)
        return requests.delete(url=url, headers=app.headers_data)

2.2 参数化数据

这里我准备了13条用户登录的测试用例, 存入data文件夹下的login.json 文件, 方便等会我们用uniitest对它进行登录接口测试参数化。

[
  {
    "desc": "case001 登录成功",
    "login_data": {
      "mobile": "13800000002",
      "password": "123456"
    },
    "status_code": 200,
    "success": true,
    "code": 10000,
    "message": "操作成功"
  },
  {
    "desc": "case002 不输入手机号",
    "login_data": {
      "mobile": "",
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case003 不输入密码",
    "login_data": {
      "mobile": "13800000002",
      "password": ""
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case004 手机长度小于11位",
    "login_data": {
      "mobile": "1380000000",
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case005 手机场地大于11位",
    "login_data": {
      "mobile": "138000000023",
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case006 手机号输入非数字",
    "login_data": {
      "mobile": "error",
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case007 输入未注册手机号",
    "login_data": {
      "mobile": "13812321236",
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case008 多参",
    "login_data": {
      "mobile": "13800000002",
      "password": "123456",
      "haha": "xixi"
    },
    "status_code": 200,
    "success": true,
    "code": 10000,
    "message": "操作成功"
  },
  {
    "desc": "case009 少参-缺少手机号",
    "login_data": {
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case010 少参-缺少密码",
    "login_data": {
      "mobile": "13800000002"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case011 无参",
    "status_code": 200,
    "success": false,
    "code": 99999,
    "message": "抱歉,系统繁忙"
  },
  {
    "desc": "case012 错误参数-手机号",
    "login_data": {
      "mobiel": "13800000002",
      "password": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  },
  {
    "desc": "case013 错误参数-密码",
    "login_data": {
      "mobile": "13800000002",
      "passwd": "123456"
    },
    "status_code": 200,
    "success": false,
    "code": 20001,
    "message": "用户名或密码错误"
  }
]

2.3 Unittest测试

test01_login.py 中我定义了uniitest测试类, 手动输入了13条测试用例,没有进行参数化。

# 导包
import app
import unittest
from api.login import LoginAPI
from utils import common_assert


# 创建测试类
class TestLogin(unittest.TestCase):
    # 前置处理
    def setUp(self):
        self.login_api = LoginAPI()

    # 定义测试用例
    # case001 登录成功
    def test01_case001(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "13800000002", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, True, 10000, "操作成功")

        # 提取token信息
        app.TOKEN = "Bearer " + response.json().get("data")
        print(app.TOKEN)
        app.headers_data["Authorization"] = app.TOKEN
        print(app.headers_data["Authorization"])

    # case002 不输入手机号
    def test02_case002(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case003 不输入密码
    def test03_case003(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "13800000002", "password": ""})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case004 手机长度小于11位
    def test04_case004(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "1380000000", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case005 手机场地大于11位
    def test05_case005(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "138000000023", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case006 手机号输入非数字
    def test06_case006(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "error", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case007 输入未注册手机号
    def test07_case007(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "13812321236", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case008 多参
    def test08_case008(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "13800000002", "password": "123456", "haha": "xixi"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, True, 10000, "操作成功")

    # case009 少参-缺少手机号
    def test09_case009(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case010 少参-缺少密码
    def test10_case010(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "13800000002"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case011 无参
    def test11_case011(self):
        # 调用登录接口进行登录
        response = self.login_api.login(None)
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 99999, "抱歉,系统繁忙")

    # case012 错误参数-手机号
    def test12_case012(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobiel": "13800000002", "password": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

    # case013 错误参数-密码
    def test13_case013(self):
        # 调用登录接口进行登录
        response = self.login_api.login({"mobile": "13800000002", "passwd": "123456"})
        print(response.json())

        # 断言
        common_assert(self, response, 200, False, 20001, "用户名或密码错误")

test02_login_params.py 相比test01_login.py 实现了参数化,其中buid_data函数 将读取的json数据传入一个字典。

# 导包
import json
import unittest
from api.login import LoginAPI
from parameterized import parameterized


# 构建测试数据
def build_data():
    # 指定文件路径
    json_file = "../data/login.json"

    # 打开json文件
    test_data = []
    with open(json_file, encoding="utf-8") as f:
        json_data = json.load(f)
        for case_data in json_data:
            login_data = case_data.get("login_data")
            status_code = case_data.get("status_code")
            success = case_data.get("success")
            code = case_data.get("code")
            message = case_data.get("message")
            test_data.append((login_data, status_code, success, code, message))
            print("test_data = {}".format((login_data, status_code, success, code, message)))
    return test_data


# 创建测试类
class TestLogin(unittest.TestCase):
    # 前置处理
    def setUp(self):
        self.login_api = LoginAPI()

    @parameterized.expand(build_data)
    # 定义测试用例
    def test01_login(self, login_data, status_code, success, code, message):
        # 调用登录接口进行登录
        response = self.login_api.login(login_data)
        print(response.json())

        # 断言
        self.assertEqual(status_code, response.status_code)
        self.assertEqual(success, response.json().get("success"))
        self.assertEqual(code, response.json().get("code"))
        self.assertIn(message, response.json().get("message"))

test03_employee 是员工管理模块的uniitest测试类, 测试类包含添加员工, 修改员工, 删除员工和查询员工。

# 导包
import unittest
from api.employee import EmployeeAPI
from utils import common_assert


# 创建测试类
class TestEmployee(unittest.TestCase):
    employee_id = None
    # employee_id = 1288061322868658176

    # 前置处理
    def setUp(self):
        self.employee_api = EmployeeAPI()

    # 添加员工测试用例设计
    def test01_add_employee(self):
        add_employee_data = {
            "username": "davi0709t0728066",   # 用户名唯一
            "mobile": "66612072866",           # 手机号唯一
            "timeOfEntry": "2020-07-09",
            "formOfEmployment": 1,
            "workNumber": "072866",            # 员工ID唯一性
            "departmentName": "销售",
            "departmentId": "1266699057968001024",
            "correctionTime": "2020-07-30T16:00:00.000Z"
        }

        # 获取响应结果
        response = self.employee_api.add_employee(add_employee_data=add_employee_data)
        print(response.json())

        # 断言
        # self.assertEqual(200, response.status_code)
        # self.assertEqual(True, response.json().get("success"))
        # self.assertEqual(10000, response.json().get("code"))
        # self.assertIn("操作成功", response.json().get("message"))
        # common_assert(self, response, 200, True, 10000, "操作成功")
        common_assert(self, response)  # 直接使用公共断言方法的默认值即可

        # 提取员工ID
        TestEmployee.employee_id = response.json().get("data").get("id")
        print(TestEmployee.employee_id)

    # 修改员工测试用例设计
    def test02_update_employee(self):
        update_employee_data = {"username": "rose0728"}
        # 获取响应结果
        response = self.employee_api.update_employee(TestEmployee.employee_id, update_data=update_employee_data)
        print(response.json())

        # 断言
        common_assert(self, response)  # 直接使用公共断言方法的默认值即可

    # 查询员工测试用例设计
    def test03_get_employee(self):
        # 获取响应结果
        response = self.employee_api.get_employee(TestEmployee.employee_id)
        print(response.json())

        # 断言
        common_assert(self, response)  # 直接使用公共断言方法的默认值即可

    # 删除员工测试用例设计
    def test04_delete_employee(self):
        # 获取响应结果
        response = self.employee_api.delete_employee(TestEmployee.employee_id)
        print(response.json())

        # 断言
        common_assert(self, response)  # 直接使用公共断言方法的默认值即可

2.4 工具

为了生成HTML报告,我这里直接引用了一个别已经写好的模块,并将它放在tools文件夹下, 等会我们可以直接在runner_suite.py里直接导入。
生成HTML报告链接: HTMLTestRunner.py

2.5 执行和其他

app.py

"""
这个文件夹放的是配置信息
"""

# 项目基础URL -- 测试环境
BASE_URL = "http://ihrm2-test.itheima.net"

# token
TOKEN = None

# 请求头数据
headers_data = {
    "Content-Type": "application/json",
    "Authorization": "Bearer bcfc9d2e-a575-4240-9569-f5f860619f97"
}

utils.py

# 公共断言方法
def common_assert(case, response, status_code=200, success=True, code=10000, message="操作成功"):
    case.assertEqual(status_code, response.status_code)
    case.assertEqual(success, response.json().get("success"))
    case.assertEqual(code, response.json().get("code"))
    case.assertIn(message, response.json().get("message"))

run_suite.py生成测试报告

# 导包
import time
import unittest
from scripts.test01_login import TestLogin
from scripts.test02_login_params import TestLogin2
from scripts.test03_employee import TestEmployee
from tools.HTMLTestRunner import HTMLTestRunner

# 组装测试套件
suite = unittest.TestSuite()
# 登录接口测试用例
suite.addTest(unittest.makeSuite(TestLogin))
# 登录接口测试用例参数化
suite.addTest(unittest.makeSuite(TestLogin2))
# 员工管理场景测试用例
suite.addTest(TestLogin("test01_case001"))
suite.addTest(unittest.makeSuite(TestEmployee))

# 指定测试报告的路径
# report = "./report/report-{}.html".format(time.strftime("%Y%m%d-%H%M%S"))
report = "./report/report.html"


# 打开文件流
with open(report, "wb") as f:
    # 创建HTMLTestRunner运行器
    runner = HTMLTestRunner(f, title="API Report")

    # 执行测试套件
    runner.run(suite)

生成的报告截图如下图所示:
在这里插入图片描述

3. 可持续集成

我使用Jenkins对该项目进行了可持续集成测试。Jenkins及其插件的安装说明, 可以参考这个下载链接: Jenkins资料, 提取码:xzgk。

3.1 具体操作

  1. 进入jenkins安装包所在目录(如D:\jenkins),打开一个dos窗口,运行命令 java -jar jenkins.war, 如果出现下图所示的 Jenkins is fully up and running, 说明jenkins已经正常启动。
    在这里插入图片描述

  2. 注意 java -jar jenkins.war 默认端口是8080, 在浏览器中输入 localhost:8080来进入Jenkins的登录界面, 然后输入你的账号和密码,成功登录后我们开始创建我们的 Freestyle project
    在这里插入图片描述

  3. 点击New Item 创建一个新的项目

在这里插入图片描述
然后选择 项目的类型为Freestyle project

  1. Source Code Management选择Git, 属于项目的URL和你Gitee的登录信息
    在这里插入图片描述

  2. Build这里选择使用 Windows bash comand
    在这里插入图片描述
    输入命令如下:

    call python run_suite.py
    exit 0
    
  3. 接下来就是生成HTML报告和邮件通知。 要注意的是Index page 必须填写 report.html, 这里必须与run_suite.py 代码里面一致。 Eiditable Email Notifications 这里的设置比较复杂,这里就不细说了。
    在这里插入图片描述

  4. 点击Build now 运行这个项目, 在排队的项目中点击你当前运行的项目。如果运行成功, 你能看到下图Finished: SUCCESS 的状态
    在这里插入图片描述
    最后你可以在下列路径找到刚刚生成的HTML文件
    在这里插入图片描述
    你也可以HTML Report 直接点击, 直接查看但是很有可能会乱码。 所以这里建议先退出Jenkins,使用java -Dhudson.model.DirectoryBrowserSupport.CSP= -jar jenkins.war 启动Jenkins,重复上述所有的流程。这样就可以解决乱码的情况。

在这里插入图片描述

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值