[接口测试_B] 14 pytest+requests实战-参数化

Posted 开源优测

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[接口测试_B] 14 pytest+requests实战-参数化相关的知识,希望对你有一定的参考价值。



上一篇:https://www.jianshu.com/p/d75f24e5de29

上一篇在一个py文件中,写了一堆test_开头的方法,所有数据和用例都在一个py文件中,本篇尝试读取json文件的测试数据,执行用例。

技术准备

  • httpbin:安装信息见上一篇

  • json:掌握json支持的数据格式和json的序列化操作

  • pytest:pytest的参数化方式

  • requests:requests是如何发送http请求的
    [接口测试_B] 14 pytest+requests实战-参数化


1、准备json格式的数据

  • httpbin中的示例接口都是比较简单的, 都没业务逻辑的关联啥的,按照requests.Request中要传入的参数准备的数据。

  • 可以把interface_info中的一个字典当作一个测试用例的数据。

# data.json

{

    "TestHttpMethods":

     {

        "interface_info": [

            {

                "interface_method": "get",

                "method": "get",

                "headers": null,

                "url_data": null,

                "data": {"test": "testdata"},

                "params": null,

                "auth": null,

                "cookies": null,

                "hooks": null,

                "json": null,

                "except": [200]

            },

            {

                "interface_method": "post",

                "method": "post",

                "headers": null,

                "url_data": null,

                "data": {"test": "testdata"},

                "params": null,

                "auth": null,

                "cookies": null,

                "hooks": null,

                "json": null,

                "except": [200]

            }

            ]

            },

            "TestAuth":

            {

            "interface_info": [

            {

            "interface_method": "basic-auth",

            "method": "get",

            "headers": null,

            "url_data": ["testuser", "testpasswd"],

            "data": null,

            "params": null,

            "auth": ["testuser", "testpasswd"],

            "cookies": null,

            "hooks": null,

            "json": null,

            "except": [200]

            },

            {

            "interface_method": "bearer",

            "method": "get",

            "headers": {"Authorization": "justtestauth"},

            "url_data": null,

            "data": null,

            "params": null,

            "auth": null,

            "cookies": null,

            "hooks": null,

            "json": null,

            "except": [200]

            }

        ]

    }

}

2、读取json文件中的数据

  • get_case(): 用于读取json文件中的数据,并保存为字典格式,最后用yield返回一个生成器

  • get_data(): 用于解析字典中的数据,由于后续要采用pytest中的@pytest.mark.parametrize进行参数化,所以把每组数据都保持在一个元组中,元组存于列表中

# conftest.py

import sys

sys.path.append('.')


import json, codecs, os

print(os.getcwd())


def get_case():

    with codecs.open('data.json', 'r', encoding='utf-8') as f:

        f_dict = json.load(f)

        for collection, cases in f_dict.items():

            for case in cases['interface_info']:

                yield {collection: case}


def get_data():

    cases = get_case()

    datas = []

    for case_d in cases:

        for collection, case in case_d.items():

            url_method = case['interface_method']

            method = case['method']

            headers = case["headers"]

            url_data = case['url_data'] # if case['url_data'] is None else tuple(case['url_data'])

            data = case['data']

            params = case['params']

            auth = case['auth']

            cookies = case['cookies']

            hooks = case['hooks']

            json = case['json']

            except_data = case['except']

            t = (url_method, method, headers, url_data, data, params, auth, cookies, hooks, json, except_data)

            datas.append(t)


return datas


结果:

print(type(get_case()))

print(get_data())

<class 'generator'>

[('get', 'get', None, None, {'test': 'testdata'}, None, None, None, None, None, [200]),

('post', 'post', None, None, {'test': 'testdata'}, None, None, None, None, None, [200]),

('basic-auth', 'get', None, ['testuser', 'testpasswd'], None, None, ['testuser', 'testpasswd'], None, None, None, [200]),

('bearer', 'get', {'Authorization': 'justtestauth'}, None, None, None, None, None, None, None, [200])]


3、重写一下requests的请求方法

  • 由于在json文件中,写入了接口路径的path部分和接口的请求方法,所以选择requests.Request()方法发送请求,参照Request的源码,将需要传入的参数都在__init__()构造方法中进行初始化

  • 可以看到__init__()中用了非常经典的三语表达式

  • 因为url_data和auth在json中传入的是列表,但是参数需要的实际格式是元组,所以当传入的参数不是None时,需要转换为元组

  • 这个文件中,导入了一个config.py文件,里面现在就一个参数BASE_URL = 'http://192.168.68.128:8088/',主要用于存储一些配置信息(如果后面发邮件或者连数据库啥的,配置信息也可以写在这里面)

  • url拼接:httpbin中,某些接口的url需要传入与auth数据一致的信息,所以采用urljoin进行拼接


# httpmethods.py

import sys

sys.path.append('.')

from urllib.parse import urljoin


import requests

from requests import Request, Session


import config


# print(config.BASE_URL)

class Http:

        def __init__(self,

                method=None, url=None, headers=None, files=None, data=None,

                params=None, auth=None, cookies=None, hooks=None, json=None,

                base_url=None, url_method=None, url_data=None):

                # Default empty dicts for dict params.

                data = [] if data is None else data

                files = [] if files is None else files

                headers = {} if headers is None else headers

                params = {} if params is None else params

                hooks = {} if hooks is None else hooks

                url_data = () if url_data is None else tuple(url_data)

                auth = None if auth is None else tuple(auth)

                

                self.hooks = requests.hooks.default_hooks()

                type(hooks)

                for (k, v) in list(hooks.items()):

                Request.register_hook(event=k, hook=v)

                

                self.method = method

                self.url = url

                self.headers = headers

                self.files = files

                self.data = data

                self.json = json

                self.params = params

                self.auth = auth

                self.cookies = cookies

                self.base_url = base_url

                self.url_method = url_method

                self.url_data = url_data

        

        def method_new(self):

            self.base_url = config.BASE_URL

            s = Session()

            url = urljoin(self.base_url, '/'.join((self.url_method,) + self.url_data))

            print(url)

            req = Request(method=self.method.upper(), url=url, headers=self.headers,

            files=self.files, data=self.data, params=self.params, auth=self.auth,

            cookies=self.cookies, json=self.json)

            prepped = req.prepare()

            # 如果需要设置代理,可以在s.send中添加并进行配置, 详情查看send的源码

            resp = s.send(prepped)

            return resp


4、采用pytest进行参数化

  • 导入前面准备的文件,采用pytest.mark.parametrize进行参数化

  • 实例化重写的请求发送方式,并传入参数化数据

  • 发送请求,接收结果并进行断言



5、运行pytest命令,执行用例生成测试报告


pytest -q --tb=no --html=./report.html


总结

  • 往前的一小步:学会了json文件的读取,虽然我觉得之前也是会的,但是在实际练习过程中发现,对json支持的数据类型与python之间的转换认识得仍然不够深入:

  • 不足之处:
    1、从json文件可以看出,TestHttpMethods和TestAuth存在的目的是想要表示一个测试集,但是在用例实际执行过程中没有体现出来,对于pytest的使用不熟练,还不知道应该如何结合起来;
    2、在命令行中使用pytest的命令执行用例的方式不够灵活;
    3、邮件发送、定时任务执行等等,都是必要的。



博客:https://www.jianshu.com/u/39cef8a56bf9


以上是关于[接口测试_B] 14 pytest+requests实战-参数化的主要内容,如果未能解决你的问题,请参考以下文章

[接口测试_B] 02 Pytest的简单示例

[接口测试_B] 07 Pytest的测试报告

[接口测试_B] 05 Pytest参数化处理

[接口测试_B] 06 Pytest的setup和teardown

[接口测试_B] 03 Pytest断言处理_assert和异常断言

[接口测试_B] 13 pytest+requests实战练习