美文网首页
[接口测试_B] 14 pytest+requests实战练习2

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

作者: 乐大爷L | 来源:发表于2018-07-03 01:14 被阅读198次

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

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

    技术准备

    • httpbin:安装信息见上一篇
    • json:掌握json支持的数据格式和json的序列化操作
    • pytest:pytest的参数化方式
    • requests:requests是如何发送http请求的


      image.png

    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进行参数化
    • 实例化重写的请求发送方式,并传入参数化数据
    • 发送请求,接收结果并进行断言
    # test_run.py
    import sys
    sys.path.append('.')
    
    import pytest
    
    from httpmethods import Http
    import conftest
    
    @pytest.mark.parametrize("url_method, method, headers, url_data, data, params, auth, cookies, hooks, json, except_data",
                             conftest.get_data())
    def test_case(url_method, method, headers, url_data, data, params, auth, cookies, hooks, json, except_data):
        h = Http(method=method, url_method=url_method, headers=headers,
                 url_data=url_data, data=data, params=params, auth=auth,
                 cookies=cookies, hooks=hooks, json=json)
        r = h.method_new()
        assert r.status_code == except_data[0]
    

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

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

    总结

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


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

    相关文章

      网友评论

          本文标题:[接口测试_B] 14 pytest+requests实战练习2

          本文链接:https://www.haomeiwen.com/subject/aexblftx.html