侧边栏壁纸
  • 累计撰写 31 篇文章
  • 累计创建 14 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

基于 Unittest 自动化框架实现通用断言方法的封装

AllyTester
2024-11-09 / 0 评论 / 0 点赞 / 9 阅读 / 0 字

Unittest 是 Python 原生自带的基础框架,是一种单元测试框架,提供用例的编写方法、断言方法、测试夹具、批量执行、结果汇总能力,用于接口自动化测试会存在一定的不便,比如说存在编码效率低、无日志文件、缺少数据隔离、缺少多条用例的参数化等,但是在 unittest 这样的原生框架下我们可以更方便的进行二次开发,本文主要聊聊通用断言方法的封装思路。

断言方法封装的核心思路:预期和实际 json 字符串的对齐

返回体需要校验内容:

  • 接口文档中所描述的返回字段是否存在

  • 返回体中不存在除接口文档外的多余字段

  • 返回体中字段的数据类型

  • 数据对齐:前置构建的数据和查询到的数据需要对齐

  • “子对象”也需要校验

断言方法实现的步骤

step1:先定义好期望返回体的协议

  • 对齐返回体的字段和结构

  • 仅需要校验数据类型的字段时,在预期返回体对应参数的值直接描述数据类型,比方说 "userId": int

  • 需要同时校验数据类型和精准值的字段时,直接在预期返回体对应参数的值描述精准值即可,比方说"userId": 1

  • 如果存在嵌套结构,也需要一一对齐数据结构和值描述在预期值当中,递归实现

# A接口文档
# 返回体demo
{
    "code": 0,
    "msg": "error params",
    "members": 
    [
        {
            "userId": 123
        }
    ]
}
# 预期返回体
{
    "code": int,
    "msg": str,
    "members": 
    [
        {
            "userId": 123
        }
    ]
}

step2:实现方法框架

  • 断言还是会应用unittest.TestCase下的断言方法,所以方法封装涉及到类继承

  • 设计了形参

class AssertCommon(unittest.TestCase):
    def func(self, expect, actual):
        """
        json通用断言方法
        :param expect: 定义预期返回体
        :param actual: 实际返回的json
        :return: 断言成功返回None,断言失败触发fail
        """

step3:实现字段存在的遍历

        # 字段是否存在的校验
        for key, value in expect.items():
            self.assertIn(key, actual.keys())

step4:检测是否存在协议外的其他字段

        # 校验是否存在多余的字段
        self.assertEqual(len(expect.keys()), len(actual.keys()),
                         msg=f'response keys len different, response keys have: {list(actual.keys())}')

step5:实现字段类型的校验

需要先判断预期中值是否为类型,才进行类型校验

        for key, value in expect.items():
            # 进行数据类型的校验
            if isinstance(value, type):
                self.assertEqual(value, type(actual[key]),
                                 msg=f'{key} type error! actual type is {str(type(actual[key]))}')

step6:实现字段精准值的校验

            else:
                self.assertEqual(value, actual[key],
                                 msg=f'{key} value error! actual value is {str(actual[key])}')

step7:遇到嵌套的列表结构时

也需要进行类型的校验和精准值的校验,根据列表索引找到对应值

            elif isinstance(value, list):
                for i in range(len(value)):
                    if isinstance(value[i], type):
                        self.assertEqual(value[i], type(actual[key][i]),
                                         msg=f'list element {actual[key][i]} type different, actual response {actual[key]}')
                    else:
                        self.assertEqual(value[i], actual[key][i],
                                         msg=f'list element {actual[key][i]} value different, actual response {actual[key]}')

step8:遇到列表字典的嵌套结构,需要实现递归

            elif isinstance(value, list):
                for i in range(len(value)):
                    if isinstance(value[i], type):
                        self.assertEqual(value[i], type(actual[key][i]),
                                         msg=f'list element {actual[key][i]} type different, actual response {actual[key]}')
                    elif isinstance(value[i], dict):
                        self.func(value[i], actual[key][i])

整体代码实现

import unittest
from common.caseOutput import error


class AssertCommon(unittest.TestCase):
    # 状态码校验
    def code_assert(self, expect, actual):
        if expect != actual:
            text = f'res code different, expect code: {expect}, actual code: {actual}.'
            error('assert fail! ' + text)
            self.fail(text)

    def __assertEqual(self):
        pass

    def json_assert(self, expect, actual):
        """
        json通用断言方法
        :param expect: 定义预期返回体
        :param actual: 实际返回的json
        :return: 断言成功返回None,断言失败触发fail
        """
        # 字段是否存在的校验
        for key, value in expect.items():
            self.assertIn(key, actual.keys())
        # 校验是否存在多余的字段
        self.assertEqual(len(expect.keys()), len(actual.keys()),
                         msg=f'response keys len different, response keys have: {list(actual.keys())}')
        for key, value in expect.items():
            # 进行数据类型/精准值的校验
            if isinstance(value, type):
                # 是基础类型,类型校验
                self.assertEqual(value, type(actual[key]),
                                 msg=f'{key} type error! actual type is {str(type(actual[key]))}')
            elif isinstance(value, list):
                # 是一个列表
                for i in range(len(value)):
                    # 根据列表中元素的类型做不同处理逻辑
                    if isinstance(value[i], type):
                        self.assertEqual(value[i], type(actual[key][i]),
                                         msg=f'list element {actual[key][i]} type different, actual response {actual[key]}')
                    elif isinstance(value[i], dict):
                        # 是一个字典,我们本方法就是json字典的断言,递归调用方法本身
                        self.json_assert(value[i], actual[key][i])
                    else:
                        # 精准值校验
                        self.assertEqual(value[i], actual[key][i],
                                         msg=f'list element {actual[key][i]} value different, actual response {actual[key]}')
            else:
                # 不是基础类型,也不是列表,是一个精准值,进行精准值校验
                self.assertEqual(value, actual[key],
                                 msg=f'{key} value error! actual value is {str(actual[key])}')

用例实现demo

import ...


@class_case_decoration
class GetCalendarNotesMajor(unittest.TestCase):
    ac = AssertCommon()
    br = BusinessRe()
    env_config = YamlRead().env_config()
    api_config = YamlRead().api_config()
    host = env_config['host']
    user_id = env_config['user_id']
    wps_sid = env_config['wps_sid']
    url = host + api_config['get_remind_note']['path']

    def setUp(self) -> None:
        """实现数据清理方法"""
        all_notes_clear(user_id=self.user_id, sid=self.wps_sid)

    def test01_major(self):
        """主流程:查看2月日历便签"""
        info('【前置步骤】请求上传/更新便签主体和内容接口,构建一条日历便签')
        DataCreate().note_create(num=1, user_id=self.user_id, sid=self.wps_sid, remind_time=1739980800000)

        info('【step】请求查看日历便签接口')
        body = {
            "remindStartTime": 1738339200000,
            "remindEndTime": 1740758400000,
            "startIndex": 0,
            "rows": 1
        }
        res = self.br.post(url=self.url, sid=self.wps_sid, user_id=self.user_id, body=body)
        expect = {
            "responseTime": int,
            "webNotes": [
                {
                    "noteId": str,
                    "createTime": int,
                    "star": 0,
                    "remindTime": int,
                    "remindType": int,
                    "infoVersion": 1,
                    "infoUpdateTime": int,
                    "groupId": None,
                    "title": str,
                    "summary": str,
                    "thumbnail": None,
                    "contentVersion": 1,
                    "contentUpdateTime": int
                }
            ]
        }
        self.ac.code_assert(200, res.status_code)
        self.ac.json_assert(expect=expect, actual=res.json())

0

评论区