接口自动化测试框架ApiRunner实战+框架解析

目录

框架结构

快速尝试

测试文件

测试用例yaml文件

RequestUtil

初始化

standand_yaml

 send_request

read_testcase

参数化

用例关联

断言

test文件封装

pytest配置文件

 conftest.py

 config.yaml

测试报告

 总结


本文介绍一个接口自动化测试框架,只需要编写yaml文件,即可实现接口自动化测试。阅读这个框架的结构并了解代码实现。由于是一个无名的框架,我把它起名叫做ApiRunner。

框架结构

接口自动化测试框架ApiRunner实战+框架解析_第1张图片

 

快速尝试

程序入口run.py,调用的pytest库执行用例。

接口自动化测试框架ApiRunner实战+框架解析_第2张图片

执行后,可以看到通过了一条用例。

接口自动化测试框架ApiRunner实战+框架解析_第3张图片

下面看看测试用例如何执行的。

测试文件

test_login.py中为测试用例

接口自动化测试框架ApiRunner实战+框架解析_第4张图片

 

 该文件为一个测试类,无继承,每一个test为一个测试函数,这也是pytest中的概念。

该测试函数,采用parametrize进行数据驱动,用read_testcase函数读取login.yaml文件,并起名为caseinfo。

在测试函数中,调用RequestUtil类,传入base_test_url,然后调用standard_yaml函数,解析caseinfo。

测试用例yaml文件

login.yaml文件,按照关键字写好接口测试用例,如name,request,validate部分。 

接口自动化测试框架ApiRunner实战+框架解析_第5张图片

RequestUtil

初始化

测试用例在初始化该类时,传入的base_test_url来自conifg.yaml文件,作用为读取baseurl的值。

接口自动化测试框架ApiRunner实战+框架解析_第6张图片

接口自动化测试框架ApiRunner实战+框架解析_第7张图片

standand_yaml

解析yaml文件,发送请求并断言

 def standard_yaml(self,caseinfo):
        caseinfo_keys= caseinfo.keys()
        #判断一级关键字是否包含:name,request,validate
        if "name" in caseinfo_keys and "request" in caseinfo_keys and "validate" in caseinfo_keys:
            #判断request下面是否包含:method、url
            request_keys=caseinfo["request"].keys()
            if "method" in  request_keys and "url" in request_keys:
                print("yaml基本架构检查通过")
                method = caseinfo['request'].pop("method") #pop() 函数用于移除列表中的一个元素,并且返回该元素的值。
                url= caseinfo['request'].pop("url")
                res = self.send_request(method,url,**caseinfo['request']) #caseinfo需要解包加**
                return_text=res.text
                return_code = res.status_code
                return_json=""
                try:
                    return_json = res.json()
                except Exception as e:
                    print("extract返回的结果不是JSON格式")

                # 提取值并写入extract.yaml文件
                if "extract" in caseinfo.keys():
                    for key, value in caseinfo["extract"].items():
                        if "(.*?)" in value or "(.+?)" in value:  # 正则表达式
                            zz_value = re.search(value, return_text)
                            if zz_value:
                                extract_value = {key: zz_value.group(1)}
                                YamlUtil().write_yaml(extract_value)
                        else:  # jsonpath
                            js_value = jsonpath.jsonpath(return_json, value)
                            if js_value:
                                extract_value = {key: js_value[0]}
                                YamlUtil().write_yaml(extract_value)
                #断言:
                self.assert_result(caseinfo['validate'],return_json,return_code)
            else:
                print("在request下必须包含method,url")
        else:
            print("一级关键字必须包含name,request,validate")

 获取caseinfo中的key,判断是否包含必须的name,request,validate关键字,如果不包含,说明写的用例格式错误(这里限制了必须包含着三个关键字)
获取request中的关键字,判断是否有method和url,没有说明格式错误。
从request中取出method,url,调用send_request方法发送请求。并获取返回结果。
如果caseinfo包含extract关键字,则进行解析,将对应值写给extract.yaml文件,调用assert_result方法进行断言。。

extrat支持正则和jsonpath,提取出的key和value存放在extract.yaml文件中

接口自动化测试框架ApiRunner实战+框架解析_第8张图片

 

接口自动化测试框架ApiRunner实战+框架解析_第9张图片 

 

接口自动化测试框架ApiRunner实战+框架解析_第10张图片 

 

 

 send_request

  def send_request(self,method,url,**kwargs):
        method=str(method).lower()  #转换小写
        #基础路径的拼接和替换
        url= self.base_url + self.replace_value(url)
        print(url)
        #参数替换
        for key,value in kwargs.items():
            if key in ['params','data','json','headers']:
                kwargs[key]=self.replace_value(value)
                print(kwargs[key])
            elif key == "files":
                for file_key, file_path in value.items():
                    value[file_key] = open(file_path, 'rb')
        res = RequestUtil.sess.request(method, url, **kwargs)
        print(res.text)
        return res

read_testcase

读取yaml数据,以列表嵌套字典形式返回

def read_testcase(yaml_name):
    with open(os.getcwd() + '\\testcases\\' + yaml_name, mode='r', encoding='utf-8') as f:
        caseinfo = yaml.load(f, yaml.FullLoader)
        print((caseinfo))
        if len(caseinfo)>=2:
            return caseinfo
        else:
            if "parameterize" in dict(*caseinfo).keys():
                new_caseinfo = ddt(*caseinfo)
                return new_caseinfo
            else:
                return caseinfo

该函数读取测试用例yaml文件,读取出的caseinfo为一个列表。如果这个列表长度大于等于2,则直接返回,否则如果列表转换后的字典有parameterize关键字,则用ddt函数进行解析,返回new_caseinfo,否则直接返回caseinfo。

[{'name': '$ddt{name}', 'request': {'method': 'post', 'url': '/auth/login', 'headers': {'Content-Type': 'application/json'}, 'json': {'username': 'tester', 'password': 'tester'}}, 'validate': [{'equals': {'status_code': 200}}]}]c

参数化

在login_data里写测试数据,用parameterize关键字传进去,在测试用例理用$ddt{}进行读取。

接口自动化测试框架ApiRunner实战+框架解析_第11张图片

接口自动化测试框架ApiRunner实战+框架解析_第12张图片 

 接口自动化测试框架ApiRunner实战+框架解析_第13张图片

在ddt函数里,对caseinfo中的带$ddt{}的数据进行了替换,返回的new_caseinfo为解析ddt后的数据。

 接口自动化测试框架ApiRunner实战+框架解析_第14张图片

在测试数据文件里,还可以直接读取extract.yaml里的值,或者直接运用test.py文件中的函数,如图的调用生成时间函数。 

用例关联

在login.yaml中用extract关键字提取出token,在下一个接口中用read_extract_data进行读取。

接口自动化测试框架ApiRunner实战+框架解析_第15张图片

之所以能用${}读取值,是因为在send_request里调用了replace_value,将引用替换成了真实的值。

断言

断言目前支持,断言返回状态码和用jsonpath断言值。

方法只支持equals和contains,其他需要自己补充方法。

接口自动化测试框架ApiRunner实战+框架解析_第16张图片

 

  # 相等断言
    def equals_assert(self,value,return_code,sj_result):
        """

        :param value: 断言字典如{msg:"登录成功"}
        :param return_code: 响应状态码
        :param sj_result: 响应json数据
        :return:
        """
        flag=0
        for assert_key,expect_value in value.items():
            print(assert_key,expect_value)
            if assert_key=="status_code":  #状态断言
                expect_value==return_code
                if expect_value!=return_code:
                    flag=flag+1
                    print("断言失败,返回的状态码不等于%s"%expect_value)
            else:#用jsonpath进行断言
                lists=jsonpath.jsonpath(sj_result,'$..%s'%assert_key)
                if lists:
                    if expect_value not in lists:
                        flag=flag+1
                        print("断言失败:"+assert_key+"不等于"+str(expect_value))
                else:
                    flag = flag + 1
                    print("断言失败:返回的结果不存在:"+assert_key)
        return flag
    # 包含断言
    def contains_assert(self,value,sj_result):
        flag=0
        if str(value) not in str(sj_result):
            flag = flag + 1
            print("断言失败:返回的结果中不包含:"+str(value))
        return flag

test文件封装

test文件封装的是常用的一些方法,可以直接用${函数名}在测试用例中调用,之所以能调用,是因为在replace_value中进行了值替换。

接口自动化测试框架ApiRunner实战+框架解析_第17张图片

pytest配置文件

pytest.ini文件是pytest的主配置文件,可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行。

接口自动化测试框架ApiRunner实战+框架解析_第18张图片

 

 conftest.py

conftest可以写一些fixture,做一些全局的前后置操作,如清理测试数据,如UI自动化测试中的打开浏览器。

不需要import导入 conftest.py,pytest用例会自动识别该文件,放到项目的根目录下就可以全局目录调用了

接口自动化测试框架ApiRunner实战+框架解析_第19张图片

 config.yaml

配置文件,可以写一些url,username等配置。

接口自动化测试框架ApiRunner实战+框架解析_第20张图片

 

测试报告

在run.py里写上allure生成报告的两句命令,执行后即可自动显示allure报告。

接口自动化测试框架ApiRunner实战+框架解析_第21张图片 接口自动化测试框架ApiRunner实战+框架解析_第22张图片

 总结

这个框架总体来说比较小而美,使用起来简单,结构基本齐全。

缺陷是,框架还有很多优化的空间,如没有封装日志,打印提示很不友好,很多地方没有进行异常处理,断言只支持equals,有兴趣可以继续优化。

 

你可能感兴趣的:(#,python接口自动化,接口自动化,python,自动化)