Python接口自动化测试框架【九】

本文为博主原创,未经许可严禁转载。
本文链接:https://blog.csdn.net/zyooooxie/article/details/113751095

之前写完 优化篇【一】 ,我想着好像也没太多东西 再优化了,然后又被打脸了,今天再来说一期。

接口自动化测试框架的 category,有兴趣,可以看看。

个人博客:https://blog.csdn.net/zyooooxie

使用不同登录账号

现在已写好的脚本中,登录账号是写死的,此账号为测试环境的白名单用户;

只是实际接口测试,涉及登录+权限,肯定要考虑不同账号的情况。

先看下 财务系统真实地设计:

整个系统因为涉及到不同模块(左面部分),要控制权限,故而 为不同用户 设计 不同角色(右面部分)。

在这里插入图片描述

我将其分类 如下:

A.无法登录的【无关账号】
B.可登录,但只有某些模块访问权限的【某些模块有权限,其他模块没有权限的普通账号】
C.可登录,所有模块都可访问的【Admin账号】

思路

实际使用时,我将其分类为:

A.【登录不成功】不登录 or 登录使用的错误账密
B.【登录成功】有权限、无权限

在excel中增加一列,保存某些想使用的账号 【不填写 默认使用白名单用户、0代表不登录】;

实际 执行当前用例时,先使用此账号登录 后跑此条用例。

代码

在这里插入图片描述

login_logout.py

class LoginLogout(object):
    url_image = '/api/auth/get/verify/image'
    url_key = '/api/auth/get/key'
    url_login = '/api/auth/login'
    url_logout = '/api/auth/logout'

    def __init__(self, env=CommonFun.read_config(section='environment', option='value'), phone=12345678900, pwd='csdnzyooooxiecsdnzyooooxie'):
        self.phone = phone
        self.password = pwd

        self.host = CommonFun.read_config(section=env, option='host')
        self.port = CommonFun.read_config(section=env, option='port')
        if self.port == '':     # 生产环境
            self.Host = ''.join(['http://', self.host])
        else:
            self.Host = ''.join(['http://', self.host, ':', self.port])

        if env == 'test' or env == 'product':
            self.url_image_new = ''.join([self.Host, self.url_image])
            self.url_key_new = ''.join([self.Host, self.url_key])
            self.url_login_new = ''.join([self.Host, self.url_login])
            self.url_logout_new = ''.join([self.Host, self.url_logout])

        else:
            raise Exception('环境传参 有误')

    def login(self):
        session = requests.session()
        res_key = session.get(self.url_key_new)
        return_key = res_key.json()['data']
        assert res_key.status_code == 200

        if self.phone != 12345678900:
            Log.info('请手输验证码,谢谢!!!')

            res2 = session.get(url=self.url_image_new, params={
    
    'key': return_key})
            assert res2.headers['Content-Type'] == 'image/jpeg'
            code_str = input('image 验证码: ')
        else:
            code_str = '1234'

        res_login = session.get(url=self.url_login_new, params={
    
    'code': code_str, 'key': return_key,
                                                                'password': self.password, 'phone': self.phone})
        assert res_login.status_code == 200
        assert res_login.json()['msg'] == 'success'
        Log.info('登录成功')

        return session, self.Host

多次使用某账号

若某账号,常常使用,总不能一直不停登录吧?

思路

python pickle 序列化和反序列化

使用时,先查询相关文件;

有,读取文件里的session;没有,登录后,保存session;

跑完所有用例,退出所有账号+删掉所有相关文件;

代码


    @staticmethod
    def change_account_session(default_session, account):

        if account == '':
            Log.debug('不填写 代表 默认账号')
            return default_session
        elif isinstance(account, float) and int(account) == 0:
            Log.debug('0 代表 不登陆,直接请求')
            return None

        elif isinstance(eval(account), tuple) and len(eval(account)) == 2:
            ph, pwd = eval(account)
            Log.debug('当前账号+密码:{}、{}'.format(ph, pwd))

            os.chdir(pickle_dir)

            all_files = os.listdir(pickle_dir)
            all_files_dict = dict.fromkeys(all_files, 'zyooooxie')

            if all_files_dict.get('{}.pkl'.format(ph)):
                Log.info('当前账号已经登录过,直接load')
                f = open('{}.pkl'.format(ph), 'rb')
                req_session = pickle.load(f)

            else:
                req_session, req_host = LoginLogout(phone=ph, pwd=pwd).login()
                Log.info('当前账号第一次登录,准备dump')

                f = open('{}.pkl'.format(ph), 'wb')
                pickle.dump(req_session, f, -1)
            f.close()

            return req_session
        else:
            raise Exception('account:{} 传参有误'.format(account))

因为使用的是requests.session(),实际此条用例执行时,用例执行方法 run_case() 内都要改动:

        # 发请求
        change_account = TestRunner.change_account_session(session, account)
        if change_account is None:
            res = SendReq().req(test_url=new_url, request_type=method, test_data=new_req_data, header=header)

        else:
            new_session = change_account
            res = SessionSendReq(session=new_session).req(test_url=new_url, request_type=method, test_data=new_req_data, header=header)

conftest.py


@pytest.fixture(scope='session')
def session_host():
    warnings.simplefilter('ignore', ResourceWarning)
    env = CommonFun.read_config(section='environment', option='value')
    s = LoginLogout(env=env)
    req_session, req_host = s.login()

    yield req_session, req_host

    s.logout(req_session)

    filter_list = list(filter(lambda x: x.endswith('pkl'), os.listdir(pickle_dir)))

    for f in filter_list:
        with open(f, 'rb') as file:
            session = pickle.load(file)
            s.logout(session)

        os.remove(f)

使用不同登录账号+多次使用 实际 run

在这里插入图片描述

在这里插入图片描述

不同账号

看下实际 权限:

在这里插入图片描述

预期结果:

  1. account值为0 是不登录,直接请求,肯定401
  2. account值为默认账号,白名单,成功拿到响应
  3. account值为16000000000是Test (没有权限),连续3个请求都是401
  4. account值为15000000000是balance(有权限),成功拿到响应

在这里插入图片描述

多次使用

用例选的是 account值为15000000000

在这里插入图片描述

断言结果

在这里插入图片描述

提取响应body 新方式

之前设计的是 提取响应body 使用 正则表达式,但我在操作过程中,发现有些问题。



b = """
{"status":"1","msg":"OPERATION SUCCEED","data":[{"id":228,"createTime":1612757834049,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778093","phone":"17777778093","mail":"[email protected]","roleIds":[]},{"id":229,"createTime":1612757834369,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778604","phone":"17777778604","mail":"[email protected]","roleIds":[]},{"id":230,"createTime":1612761674400,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777777982","phone":"17777777982","mail":"[email protected]","roleIds":[]},{"id":231,"createTime":1612764630761,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778650","phone":"17777778650","mail":"[email protected]","roleIds":[]},{"id":232,"createTime":1612767472180,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777777883","phone":"17777777883","mail":"[email protected]","roleIds":[]},{"id":233,"createTime":1612767960244,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777778202","phone":"17777778202","mail":"[email protected]","roleIds":[]},{"id":234,"createTime":1612768536658,"updateTime":null,"deleted":false,"appUid":null,"userName":"17777777787","phone":"17777777787","mail":"[email protected]","roleIds":[]}]}
"""

# test = r'{"id":(\d{3}),"createTime":\d+,"updateTime":\w+,"deleted":\w+,"appUid":\w+,"userName":"17777777787'
test = r'"id":(\d{3}).*?"userName":"17777777787"'
print(re.search(test, b))


在这里插入图片描述

我的正则 是想使用 非贪婪模式,直接定位到最后一条【新建某记录后,一般都是最后一条】;

可是无法做到。查询的结果是 第一个id 到最后一条的userName。

从头就直接匹配上了,但想匹配 最后的“userName":"17777777787, 没辙,就得匹配到最后

当然可以换个正则表达式,
如下:

在这里插入图片描述

但我还是想 用python写方法 去提取body。

思路

一般我这儿接口返回的是 某个字典list【下图的data字段值】

在这里插入图片描述

在这里插入图片描述

2个想法:

  1. 直接取 list的最后一个元素,再使用dict的get() ;
  2. 直接获取整个字典list,根据某字段=某值,找到所需要的dict,再使用get();

代码

在这里插入图片描述



    @staticmethod
    def response_function_extractor(res: Response, extract, params_dict):
        for e in extract:
            Log.info(e)
            if len(e) == 2:

                # e[0] 含res_json
                assert e[0].find('res_json') != -1
                res_json = res.json()

                result = eval(e[0])
                params_dict.update({
    
    e[1]: result})
                Log.info(params_dict)

            else:
                assert len(e) == 4
                # e[0] 含res_dict
                assert e[0].find('res_dict') != -1
                res_dict = res.json()

                dict_list = eval(e[0])
                value = CommonFun.change_params_dictValue(e[2], params_dict)

                for d in dict_list:
                    if d[e[1]] == value:
                        result = d[e[3]]

                        params_dict.update({
    
    e[3]: result})
                        Log.info(params_dict)

                        break
                else:
                    raise Exception('提取失败:{}'.format(extract))

        return params_dict

当然 run_case() 也要改;

我不太推荐 一次搞2种,我担心再出啥bug。


        # 后置提取【最多一种】
        if re_extractor != '':
            # 正则
            assert function_extractor == ''
            res_text = res.text
            re_extractor = eval(re_extractor)

            params_actual_value_dict = CommonFun.response_re_extractor(res_text, re_extractor, params_actual_value_dict)

        if function_extractor != '':
            assert re_extractor == ''
            # 方法
            function_extractor = eval(function_extractor)

            params_actual_value_dict = CommonFun.response_function_extractor(res, function_extractor, params_actual_value_dict)

提取响应body新方式 实际 run

这里再综合上述 不同账号+权限。
使用的是 财务系统的增加用户、查询用户list 、删除用户的接口【只有Admin账号 才有权限】;

在这里插入图片描述

在这里插入图片描述

使用正则表达式 提取

在这里插入图片描述

在这里插入图片描述

使用python方法 提取

在这里插入图片描述

在这里插入图片描述

整个过程

在这里插入图片描述

这次分享就主要这些内容;

我真的感觉没啥可再优化了。

希望别打脸。

交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie

猜你喜欢

转载自blog.csdn.net/zyooooxie/article/details/113751095