颠覆你的Python实践Python接口自动化测试软件测试

[接口测试-实战]03 python3 怎么对接口数据做断言

2017-08-30  本文已影响271人  亭子青年

想要改变世界,从改变自己开始,大家好,我还是那个不怎么会带娃,不怎么会测试的伪测试。

今天继续整理关于《python3+mysql+requests》实战的第三篇文章,前面两篇分别介绍了python3怎么和mysql进行交互,怎么通过logging包来输出日志,怎么通过requests包来发送http请求,第三篇文章主要介绍对接口返回的数据进行断言操作。

文章列表
《[接口测试-实战]01 py+mysql实战》
《[接口测试-实战]02 py+requests实战》

概述

首先介绍一下上下文关系,我们的测试用例是保存在mysql数据表(case_interface)中,关于测试用例的写入我们会写一个基于Django的前端页面来进行操作,但是这里暂时不讨论怎么写入,主要说说怎么通过数据表(case_interface)中保存的数据进行接口的断言。下面是我们创建数据表的sql语句,里面有相应的注释,就不依次对各个字段进行解读了。

CREATE TABLE `case_interface` (
  `id` int(2) NOT NULL AUTO_INCREMENT,
  `name_interface` varchar(128) NOT NULL COMMENT '接口名称',
  `exe_level` int(2) DEFAULT NULL COMMENT '执行优先级,0代表BVT',
  `exe_mode` varchar(4) DEFAULT NULL COMMENT '执行方式:post,get,默认是post方式',
  `url_interface` varchar(128) DEFAULT NULL COMMENT '接口地址:如果以http开头的则直接使用改地址,否则拼接上不同环境的ip前缀作为接口地址',
  `header_interface` text COMMENT '接口请求的头文件,有则使用,无则调用配置文件',
  `params_interface` varchar(256) DEFAULT NULL COMMENT '接口请求的参数',
  `result_interface` text COMMENT '接口返回结果',
  `code_to_compare` varchar(16) DEFAULT NULL COMMENT '待比较的code值,用户自定义比较值,例如returncode和code等,默认returncode',
  `code_actual` varchar(16) DEFAULT NULL COMMENT '接口实际code实际返回值',
  `code_expect` varchar(16) DEFAULT NULL COMMENT '接口预期code返回值',
  `result_code_compare` int(2) DEFAULT NULL COMMENT 'code比较结果,0-pass,1-fail,2-无待比较参数,3-比较出错,4-返回包不合法,5-系统异常'
  `params_to_compare` varchar(256) DEFAULT NULL COMMENT '接口比较参数集合,用于比较参数完整性',
  `params_actual` text COMMENT '接口实际返回参数',
  `result_params_compare` int(2) DEFAULT NULL COMMENT 'code比较结果,0-pass,1-fail,2-获取参数集错误:去重错误,5-系统异常',
  `case_status` int(2) DEFAULT '0' COMMENT '用例状态 0-有效,1-无效',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='接口用例表';

【半分离】关于接口测试的断言,我们使用半分离而非全分离的方式,那么什么是半分离呢,就是接口自动化里面不包含对接口业务逻辑的验证,因为如果对业务逻辑进行验证,我们的性价比会很低,维护成本非常高,没有必要,所以我们主要对接口进行参数校验。(看来下一篇文章,我们要来说说怎么写接口测试用例了)

【断言】我们回归正题,我们断言哪些数据?怎么断言?
大家可以看到上面的sql语句中有几个关键的字段

 `code_to_compare` varchar(16) DEFAULT NULL COMMENT '待比较的code值,用户自定义比较值,例如returncode和code等,默认returncode',
  `code_actual` varchar(16) DEFAULT NULL COMMENT '接口实际code实际返回值',
  `code_expect` varchar(16) DEFAULT NULL COMMENT '接口预期code返回值',
  `result_code_compare` int(2) DEFAULT NULL COMMENT 'code比较结果,0-pass,1-fail,2-无待比较参数,3-比较出错,4-返回包不合法,5-系统异常',
  `params_to_compare` varchar(256) DEFAULT NULL COMMENT '接口比较参数集合,用于比较参数完整性',
  `params_actual` text COMMENT '接口实际返回参数',
  `result_params_compare` int(2) DEFAULT NULL COMMENT 'code比较结果,0-pass,1-fail,2-获取参数集错误:去重错误,5-系统异常',

看到sql语句之后,应该有一个初步的想法了。我们的接口测试主要是对接口返回的json数据的code和参数完整性做断言,code就是接口返回的标志该接口处理结果的一个唯一标志,根据接口文档获得,参数,就是所有返回的key关键字进行断言,看预期的关键字(关键字列表)是否都包含在我们json返回包中。

根据这个思路构造了名为compare_param的类,类中包含了三个主要方法
(1)def compare_json_code(self,case_data,result_data):pass =》当接口返回结果是json格式的数据,通过这个方法,你可以对比返回的状态码code,并将结果写回数据库
(2)def compare_json_params(self,case_data,result_data):pass=》当接口返回结果是json格式的数据,通过这个方法,你可以对比返回的参数和数据库中的预期参数,并将结果写入数据库中
(3) def jsondata_to_set(self,dict_ob, set_ob):pass=》将json格式的数据进行去重操作:这是一个递归

我跟着大婶的步伐,画了两个不像流程图的流程图,一个是code比较的,一个是参数完整性比较的。

亭子关于接口断言画的伪流程图.jpg

code

sql语句,类定义框架,流程图都画好了,我们就上代码了。

#opresponce.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'''
@author:ht
@desc:根据数据表中的信息对response的结果做断言(code,参数完整性)操作,并将断言结果写入数据表中
思考:接口返回的对象有可能不是json哦,比如xml,那么我们这个类应该具有可扩展性才对

'''
from mylogging import mylogger
from opmysql import OpMysql
import json

class compare_param:
    def compare_json_code(self,case_data,result_data):
        '''
        当接口返回结果是json格式的数据,通过这个方法,你可以对比返回的状态码code,并将结果写回数据库
        :param case_data: dict=>数据库中存储的测试case的数据,一条数据
        :param result_data: dict=》调用接口返回的json格式的数据
        :return:
        1.{'code': 200, 'message': 'code相等', 'content': ''}
        2.{'code': 300, 'message': 'code不相等', 'content': ''}
        3.{'code': 301, 'message': 'code不存在', 'content': ''}
        4.{'code': 400, 'message': 'code比较出现异常', 'content': ''}
        '''
        try:
            op_sql = OpMysql()
            update_sql = "UPDATE case_interface SET result_interface=%s ,code_actual=%s ,result_code_compare=%s WHERE id = %s"

            code_expected = case_data.get("code_to_compare") #获取数据库中保存的code值
            if 'resultCode' in result_data:
                result_code = result_data.get("resultCode")# 获取返回结果中的code值
                if code_expected == result_code:
                    op_sql.op_sql(update_sql,(str(result_data),result_code,0,case_data.get("id"))) # code比较结果,0-pass
                    result = {'code': 200, 'message': 'code相等', 'content': ''}
                else:
                    op_sql.op_sql(update_sql, (str(result_data), result_code, 1, case_data.get("id"))) # 1-fail
                    result = {'code': 300, 'message': 'code不相等', 'content': ''}
            else:
                op_sql.op_sql(update_sql, (str(result_data), None, 2, case_data.get("id"))) #2-无待比较参数
                result = {'code': 301, 'message': 'code不存在', 'content': ''}
        except BaseException as e:
            op_sql.op_sql(update_sql, (str(result_data), None, 4, case_data.get("id"))) # 5-系统异常
            mylogger.info("[compare_json_code 测试用例的信息]-%s"%str(case_data))
            mylogger.info("[compare_json_code 接口返回的信息]-%s"%str(result_data))
            mylogger.exception(e)
            result = {'code': 400, 'message': 'code比较出现异常', 'content': ''}

        return result
    def jsondata_to_set(self,dict_ob, set_ob):
        '''
        将json格式的数据进行去重操作:这是一个递归
        :param dict_ob: dict:json格式的数据
        :param set_ob: 传入的set对象(没有内容)
        :return:
        1.{"code":200,"message":"json:参数去重成功","content":set_ob}
        2.{"code": 400, "message": "json:参数去重出现异常", "content": ""}
        '''
        try:
            for key, value in dict_ob.items():
                set_ob.add(key)
                if isinstance(value, dict):
                    self.jsondata_to_set(value, set_ob)
                elif isinstance(value, list):
                    for li in value:
                        self.jsondata_to_set(li, set_ob)

            result = {"code":200,"message":"json:参数去重成功","content":set_ob}
        except BaseException as e:
            mylogger.info("[jsondata_to_set params]-(dict_ob:%s,set_ob:%s)"%(str(dict_ob),str(set_ob)))
            mylogger.exception(e)
            result = {"code": 400, "message": "json:参数去重出现异常", "content": ""}
        return result
    def compare_json_params(self,case_data,result_data):
        '''
        当接口返回结果是json格式的数据,通过这个方法,你可以对比返回的参数和数据库中的预期参数,并将结果写入数据库中
        :param case_data: 数据库返回的用例信息,dict=》一条数据
        :param result_data: 接口返回的json数据,dict
        :return:
        1. {"code": 200, "message": "参数完整性相等", "content": ""}
        2. {"code": 301, "message": "参数完整性不相等", "content": ""}
        3. {"code": 300, "message": "去重失败:获取参数集错误", "content": ""}
        4. {"code": 400, "message": "参数完整性比较出现异常", "content": ""}
        '''
        try:
            op_sql = OpMysql()
            # 获取预期参数
            case_params = case_data.get("params_to_compare")
            case_params_set = eval(case_params)
            #更新数据的sql语句
            update_sql = "UPDATE case_interface set params_actual=%s, result_params_compare=%s WHERE id=%s"

            #对json数据进行去重操作
            params_set = set()
            set_result = self.jsondata_to_set(result_data,params_set)
            if set_result.get("code") == 200:
                #判断是否包含
                if case_params_set.issubset(params_set):
                    #跟新数据表
                    op_sql.op_sql(update_sql,(str(params_set),0,case_data.get("id"))) #0 pass
                    result = {"code": 200, "message": "参数完整性相等", "content": ""}
                else:
                    op_sql.op_sql(update_sql,(str(params_set),1,case_data.get("id"))) #1 fail
                    result = {"code": 301, "message": "参数完整性不相等", "content": ""}
            elif set_result.get("code") == 400:
                op_sql.op_sql(update_sql, (None, 2, case_data.get("id")))  # 2 去重错误
                result = {"code": 300, "message": "去重失败:获取参数集错误", "content": ""}
                # raise BaseException("我就是测试一下罗")
        except BaseException as e:
            op_sql.op_sql(update_sql, (None, 5, case_data.get("id")))  # 5 系统异常
            mylogger.info("[compare_json_params params -(case_data:%s,result_data:%s)]"%(str(case_data),str(result_data)))
            mylogger.exception(e)
            result = {"code": 400, "message": "参数完整性比较出现异常", "content": ""}
        return result

    # (预留方法):当接口返回结果是xml格式的数据的时候,可以使用这个方法来对比返回的状态码code,并将结果写回到数据库
    def compare_xml_code(self):
        pass
    # (预留方法):当接口返回结果是xml格式的数据,通过这个方法,你可以对比返回的参数和数据库中的预期参数,并将结果写入数据库中
    def compare_xml_params(self):
        pass

if __name__ == "__main__":
    #获取数据库的测试用例的信息:id=1
    mysql_op = OpMysql()
    case_data = mysql_op.select_one("SELECT * FROM  case_interface WHERE id = 1")
    #创建一个compare对象
    cmp_obj = compare_param()

    # test:compare_json_params:参数完整性相等
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos":"测试环境店铺","resultCode": "000","totalPage": 66746}'
    # cmpare_result = cmp_obj.compare_json_params(case_data,json.loads(result_interface))
    # print(cmpare_result)
    # test:compare_json_params:参数完整性不相等
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos11":"测试环境店铺","resultCode": "000","totalPage": 66746}'
    # cmpare_result = cmp_obj.compare_json_params(case_data,json.loads(result_interface))
    # print(cmpare_result)
    # test:compare_json_params:去重失败
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos11":"测试环境店铺","resultCode": "000","totalPage": 66746}'
    # cmpare_result = cmp_obj.compare_json_params(case_data,result_interface)
    # print(cmpare_result)
    # test:compare_json_params:参数完整性比较出现异常,前提是直接在代码中raise一个异常出来,因为我也想不到这里在什么情况下会走这条分支
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos":"测试环境店铺","resultCode": "000","totalPage": 66746}'
    # cmpare_result = cmp_obj.compare_json_params(case_data,result_interface)
    # print(cmpare_result)


    #测试递归调用函数
    # dict01 = {"name":"ht","pass":"123","family":[{"name1":{"new":"new_value","haha":"ss"},"age":1},{"name2":"老公","age3":18}],"lover":{"lover01":"吃","lover02":"喝"}}
    # my_set = set()
    # result = cmp_obj.jsondata_to_set(dict01,my_set)
    # if result.get("code") == 200:
    #     print(result.get("code"))
    #     print(result.get("message"))
    #     print(result.get("content"))
    result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos": [{"phone": "15100000000","star": 5,"totalQualityEvaluation": 0,"photoUrls": "","latitude": 0},{"phone": "15200000000","detail": null,"sex": null,"serviceFrequency": 0}],"resultCode": "000","totalPage": 66746}'
    my_set = set()
    result = cmp_obj.jsondata_to_set(json.loads(result_interface),my_set)
    if result.get("code") == 200:
        print(result.get("code"))
        print(result.get("message"))
        print(result.get("content"))


    # test_compare_json_code:200-code相等
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos":"测试环境店铺","resultCode": "000","totalPage": 66746}'
    # compare_result = cmp_obj.compare_json_code(case_data, json.loads(result_interface))
    # print(compare_result)

    # test_compare_json_code:300-code不相等
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos":"测试环境店铺","resultCode": "001","totalPage": 66746}'
    # compare_result = cmp_obj.compare_json_code(case_data, json.loads(result_interface))
    # print(compare_result)

    # test_compare_json_code:301-code不存在
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos":"测试环境店铺","totalPage": 66746}'
    # compare_result = cmp_obj.compare_json_code(case_data, json.loads(result_interface))
    # print(compare_result)

    # test_compare_json_code:400-code比较出现异常
    # result_interface = '{"message": "获取附近服务商成功","nextPage": 1,"pageNo": 0,"merchantInfos":"测试环境店铺","resultCode": "000","totalPage": 66746}'
    # compare_result = cmp_obj.compare_json_code(case_data, result_interface) #传入错误的参数
    # print(compare_result)

扩展

其实我们接口返回的数据不一定是json格式的,可能会是txt,xml等,那么我们怎么做断言?还是和上面思路一致,只需要扩展一下onresponce.py文件中的那个compare_param类就可以了,其实大家如果仔细看我的code的话,应该可以理解我说的意思。

写在后面的话

生活虐我千百遍,我待生活如初恋。希望我能永远保持这份执着,好好的做一个伪测试。虽然我还没有正式的工作。。。。

感谢大婶,老川(你看到我这么叫你,你会不会好伤心,昨天还是叔,今天就老了)

上一篇 下一篇

猜你喜欢

热点阅读