zoukankan      html  css  js  c++  java
  • python3接口自动化测试框架:python3+requests+数据驱动(json)

    接口测试是单元测试的一个子集,但又不等同于单元测试。从测试的角度
    来看,接口测试的价值在于其测试投入比单元测试少,而且技术难度也比单元测
    试小。一般来说,接口测试的粒度要比单元测试更粗,它主要是基于子系统或者
    子模块的接口层面的测试。因此,接口测试需要测试的接口或者函数的数量会远
    远小于单元测试,与此同时,接口定义的稳定性会远远高于类级别的函数。所以,
    接口测试用例代码的改动量也远远小于单元测试,代码维护成本会比单元测试少
    很多,因而测试的投入量会小很多。从另外一个层面来看,借助于接口测试,可
    以保证子系统或子模块在各种应用场景下接口调用的正确性,那么子系统或子模
    块的产品质量也可以得到充分的保证。因此,接口测试是一种适度的白盒测试技
    术,准确说它是一种灰盒测试,投入产出是非常理想的。
    总的来说,接口测试是保证高复杂性系统质量的内在要求和低成本的经
    济利益的驱动作用下的最佳解决方案。主要体现在下面的二个方面:
    首先,接口测试节省了测试成本。
    其次,接口测试不同于传统开发的单元测试,接口测试是站在用户的角
    度对系统接口进行全面高效持续的检测。

    (ps:以上有网络搜索和自己的总结)

    好了,话不多说,接下来我们测试接口的整体思路是这样的:

    1、把接口的相关数据保存在json文件中。

    2、我们通过去读这个json文件,来读取接口的相关信息,包括:路径、参数、请求方式、比对值等。

    3、基于requests封装一个发送请求的方法。

    4、使用装饰器来进行具体的测试。

     1 {
     2     "test_1_ip_api": {
     3         "url": "http://httpbin.org/ip",
     4         "assert": {
     5             "origin":"183.16.188.172"
     6         },
     7         "method": "get",
     8         "params": {
     9                         "a":"b"
    10 },
    11         "case": "测试httpbin的ip接口返回正常"
    12     }
    13

    以上就是Json文件的格式,一条这样的数据,代表一条用例:

    test_1_ip_api:算作是一个标识。

    url:接口的路径。

    assert:最后接口的比对值。

    method:接口请求方式。

    params:请求参数。ps:如果请求方式为post此处为data。

    case:此条用例的名字,也就是测试的点。

    (注:有些可能需要添加headers,这个用户自己添加一下就行了,但是后面的代码稍微修改一下。) 

    当然有了文件,那么我们就需要把这个数据给读出来。见如下代码。

    class ReadCaseData(object):
    
        def __init__(self,filename):
            self.filename = filename
            self.path = os.path.join(get_super_path(),'data',self.filename)
          @property
    def read_case_json(self) -> dict: ''' 获取指定文件的json数据 :return: ''' try: my_json = open(self.path,encoding='utf-8') json_data = json.load(my_json) return json_data except Exception as err: print(str(err)) def get_case_content(self,first_key,second_key): ''' 获取json相应值 ''' return self.read_case_json[first_key][second_key] def get_assert_keys(self,first_key): ''' 根据用例名取assert的keys ''' return self.get_case_content(first_key,'assert').keys() def get_assert_value(self,first_key,assert_key): ''' 取assert的具体值 ''' return self.read_case_json[first_key]['assert'][assert_key] def get_case_name(self,first_key):
            '''
            获取用例名
            '''
        return self.get_case_content(first_key,'case')

    以上就是读取json文件的各种数据,并根据需要封装了一些方法。(思路:会根据不同的json文件数据,给这个json文件取相应的文件名,那么此处的类就是把这个文件名初始化,然后读取此文件相关的一些数据。)

    (注:1、get_super_path()是写的一个获取根目录的方法。)

    那么数据读出来了,要怎么运用呢,那么我们就需要借用一下requests(这个库应该不用说了,您如果在找这样的文章,那么我肯定知道这个库罗。)。

    def get_base_url():
        if IniHelper('server.ini').get_value('server', 'select') == 'test':
            return IniHelper('server.ini').get_value('server', 'test')
        elif IniHelper('server.ini').get_value('server', 'select') == 'formal':
            return IniHelper('server.ini').get_value('server', 'formal')
        else:
            raise AttributeError("配制文件读取出错!请检查!")
    
    
    class Myrequests(ReadCaseData):
    
        def __init__(self, filename, case_name):
            ReadCaseData.__init__(self, filename)
            self.case_name = case_name
            self.base_url = get_base_url()
    
    
        def make_requests_template(self):
            '''
            根据读取到的json文件中的method,来发送不到的请求,目前只能发送get和post请求
            :return:
            '''
            if self.get_case_content(self.case_name, 'method').lower() == 'get':
                body = {}
                body['url'] = self.base_url + self.get_case_content(self.case_name, 'url')
                body['params'] = self.get_case_content(self.case_name, 'params')
                return self.get(**body)
            elif self.get_case_content(self.case_name, 'method').lower() == 'post':
                body = {}
                body['url'] = self.base_url + self.get_case_content(self.case_name, 'url')
                body['params'] = self.get_case_content(self.case_name, 'data')
                return self.post(**body)
            else:
                raise AttributeError("错误的请求方法, 请检查配置文件中的请求方法, 目前只支持['GET', 'POST']")
    
        def get(self, **kw):
            '''
            使用requests发送get请求
            :param kw: 参数需要传一个字典表
            :return: 
            '''
            return requests.get(**kw)
    
        def post(self, **kw):
            '''
            使用requests发送post请求
            :param kw:参数需要传一个字典表
            :return:
            '''
            return requests.post(**kw)
    
        @property
        def get_json(self):
            '''
            返回接口的json数据
            :return:
            '''
            try:
                return self.make_requests_template().json()
            except Exception as e:
                print('json format error' + str(e))
    
        @property
        def get_status_code(self):
            '''
            返回接口发送后的status_code值
            :return:
            '''
            return self.make_requests_template().status_code

    这里首先是继承了我们刚刚封装的读取的json文件的类,因为继承过后就可以使用他的一些方法,然后通过读取里面的数据来发送请求等。

    注:我这里的接口的服务器地址是通过读取配置文件里的,这个代码就不分享了,大家网找一下,很多。(随意啦)

    ps:1、IniHelper是封装的一个读取配置文件的类。

      2、此处只封装了get和post,当然也可以继续添加其它(比如:put、delete等等!!)

    数据也读出来了,发送请求的方法也封装好了,那么接下来要怎么用这些数据来测试呢?

    def test_case_run(data_file_name, test_case_key):
        def _test_case_run(func):
            def wrap(self):
                r = Myrequests(filename=data_file_name, case_name=test_case_key)
                self.r = r
                self._testMethodDoc = r.get_case_name(test_case_key)
                self.status_code = r.get_status_code
                self.json = r.get_json
                self.assert_key = r.get_assert_keys(test_case_key)
                log.get_log(test_case_key).info(f'开始测试:{test_case_key}')
                log.get_log(test_case_key).info('比对:status_code')
                self.assertEqual(r.get_status_code, 200)
                for key in r.get_assert_keys(test_case_key):
                    log.get_log(test_case_key).info(f'比对:{key}')
                    self.assertEqual(get_dict_value(key, **r.get_json), r.get_assert_value(test_case_key, key))
                func(self)
    
            return wrap
    
        return _test_case_run

    以上就是根据读取的数据和发送请求的方法封装的一个装饰器。

    其中:

    log:这个是基于logging封装的记录log的类。

    get_assert_value():这个是写的一个根据json数据里读出来的assert的数据里面的Key,来获取实际接口里面返回的value的方法。

    (注:1、json里面的assert的数据至少一条,有可能多条。2、使用Key从实际结果中获取value时,可能是多级字典,因此此方法使用了递归)

    最后就是我们使用python的unittest来进行测试了。

    from src.testcase.method.base_test import BaseTest
    from src.testcase.method import wraps
    import unittest
    
    
    class TestApi(BaseTest):
    
        @wraps.test_case_run('data.json', 'test_1_ip_api')
        def test_01(self):
            self._testMethodDoc = self._testMethodDoc
    
        @wraps.test_case_run('data.json', 'test_2_headers_api')
        def test_02(self):
            self._testMethodDoc = self._testMethodDoc
    
        @wraps.test_case_run('data.json', 'test_3_post_api')
        def test_03(self):
            self._testMethodDoc = self._testMethodDoc
    
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)

    以上就是实际具体的用例,我们在每一条case上使用装饰器,然后装饰器有二个参数,这里说明一下:1、就是此case对应的文件名。2、就是此case在json文件中对应的标识。

    建议:

    1、每一个接口对应一个json文件,然后在这个json文件中为每一条case添一个标识。(文中的Json文件就是一个接口对应的一条case。)

    2、所有的json文件放在同一个文件夹下。

    3、每一个接口对应一个测试文件。

    4、结果最后采用htmltestrunner的html报告输出。

    5、把报告通过email发送。

    6、使用jenkins在服务器部署。

    欢迎大家指证!!!

  • 相关阅读:
    可以多个广告浮动的页面
    获取内容并截取长度用...替换
    用UIManager使Swing界面转换风格。
    Java FileInputStream
    win7 部署tomcat总结
    观察者模式
    Java RandomAccessFile
    Java 客户端界面功能:停止当前操作
    多线程的开启与管理
    log4j内容记录
  • 原文地址:https://www.cnblogs.com/Alin-2016/p/8891471.html
Copyright © 2011-2022 走看看