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

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

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

技术准备

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

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 声明:本文已获作者授权转载,著作权归作者所有。

原文发布于微信公众号 - 开源优测(DeepTest)

原文发表时间:2018-07-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏magicsoar

C++内存布局(1)-让new出的两个变量在堆上的地址连续

大家都知道栈的地址按照从高到低的顺序增长的, 而堆的地址是按照从底到高的顺序增长的。 int *n1 = new int(1); int *n2 = new i...

2099
来自专栏mwangblog

python模块

1593
来自专栏闪电gogogo的专栏

Python初学——pickle & set

pickle 存放数据 将数据保存为文件是永久保存的唯一方式,而文档内部是以字符串形式进行存放的,如果我们需要保存的是一个包含很多数据甚至是类的实例化的复杂的列...

2315
来自专栏Laoqi's Linux运维专列

for 循环,while循环,break,continue,exit

3798
来自专栏博客园

Redis命令与配置

    slaveof  127.0.0.1 6379(设置Mater的Host以及Port)

1654
来自专栏C/C++基础

内存池介绍与经典内存池的实现

利用默认的内存管理函数new/delete或malloc/free在堆上分配和释放内存会有一些额外的开销。

871
来自专栏coding for love

JS入门难点解析4-执行上下文栈

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

1164
来自专栏Python

nginx配置 location及rewrite规则详解

1712
来自专栏python 实践经验

python 语法基础之字符集编码

Python初学者编码实践中经常遇到encode error,decode error。

4725
来自专栏Deep learning进阶路

C++随记(九)---名称空间

C++随记(九)---名称空间 C++中,名称可以是变量、函数、结构、类等等,项目增大会使得名称冲突的可能性增大,人类可用的单词数太少,并且不同的人写的程序不可...

1860

扫码关注云+社区

领取腾讯云代金券