request库基本使用
发送get请求
# 1、GET请求
r = requests.get('https://httpbin.org/ip')
print(r.text)
# 1.1 发送GET请求,带参数
#等同于直接访问https://httpbin.org/get?name=mikezhou
r = requests.get('https://httpbin.org/get', params={'name': 'mikezhou','age':18})
print(r.text)
# 1.2 定制请求头
header = {'user-agent': 'my-app/0.0.1'}
r = requests.get('https://httpbin.org/get', headers=header)
print(r.text)
# 1.3 发送get请求, 加proxy
proxies = {'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'}
r=requests.get('https://httpbin.org/get', proxies=proxies)
print(r.text)
# 1.4 发送get请求,加鉴权 -- Basic Auth
from requests.auth import HTTPBasicAuth
r = requests.get('https://api.github.com/user', auth=HTTPBasicAuth('user', 'password'))
print(r.text)
发送post请求
# 通常,你想要发送一些编码为表单形式的数据——非常像一个 HTML 表单。
# 要实现这个,只需简单地传递一个字典给 data 参数, 数据字典在发出请求时会自动编码为表单形式
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post('http://httpbin.org/post', data = payload)
print(r.text)
# 2.1 很多时候你想要发送的数据并非编码为表单形式的。
# 如果你传递一个 string 而不是一个 dict。
import json
payload = {'key1': 'value1', 'key2': 'value2'}
r = requests.post('https://api.github.com/some/endpoint', data=json.dumps(payload))
print(r.text)
# 2.2 此处除了可以自行对 dict 进行编码,
# 你还可以使用 json 参数直接传递,然后它就会被自动编码,下述和上述代码等价
r = requests.post('https://api.github.com/some/endpoint', json=payload)
print(r.text)
发送PUT请求
r = requests.put('https://httpbin.org/put', data={'name': 'mikezhou'})
print(r.text)
发送DELETE请求
r = requests.delete('https://httpbin.org/anything', data={'name': 'mikezhou'})
print(r.text)
获取接口返回值
r = requests.post('https://httpbin.org/anything', data={'hello': 'mikezhou'})
# 返回文本型response
print(r.text)
# 获取二进制返回值
print(r.content)
# 返回JSON串
print(r.json())
# 获取请求返回码
print(r.status_code)
#
# # 获取response的headers
print(r.headers)
#
# # 获取response的cookie
print(r.cookies.get_dict())
request保存session
# 初始化一个session对象
s = requests.Session()
# httpbin这个网站允许我们通过如下方式设置,在set后写你需要的值即可
s.get('https://httpbin.org/cookies/set/sessioncookie/mikezhou')
r = s.get('https://httpbin.org/cookies')
print(r.text)
postman代码转换为Request代码
开始打造一个测试框架
创建框架目录结构
◆ test_case:存放测试用例
◆ test_data:存放测试数据
◆ report:存放测试报告
◆ common:存放公共方法
◆ lib:存放第三方库
◆ config: 存放环境配置信息
◆ main:框架主入口
使用request封装http请求类
创建测试用例
第一个例子
import random
import unittest
from test_project.common.http_requests import HttpRequests
class TestBattal(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.url = 'http://127.0.0.1:12356/'
cls.http = HttpRequests(cls.url)
def setUp(self) -> None:
pass
def tearDown(self) -> None:
pass
def test_001_index(self):
'''测试访问首页'''
response = TestBattal.http.get()
self.assertEqual(response.status_code, 200, '请求返回非200')
def test_002_login(self):
'''测试登录'''
payload = {'username': 'mikezhou', 'password': 123456}
response = TestBattal.http.post('login',params=payload)
# # 获取登录返回的内容
globals()["text"] = response.text.split('\n')[1:] # global()会返回一个字典,这个字典后面的测试方法运行时也可以访问到,用来存放全局数据
print(globals()["text"])
self.assertEqual(200, response.status_code, '请求返回非200')
self.assertIn('10001', response.text, '响应不包含10001')
def test_003_select(self):
'''测试选择装备'''
# 随机选择装备
globals()["equipmentid"] = random.choice(globals()["text"]).split(':')[0]
payload = {'equipmentid': globals()["equipmentid"]}
response = TestBattal.http.post('selectEq',data=payload)
self.assertEqual(200, response.status_code, '请求返回非200')
self.assertIn('equipmentid', response.text, '响应不包含equipmentid')
def test_004_kill(self):
'''测试杀敌'''
payload = {'equipmentid': globals()["equipmentid"], 'enemyid': '20001'}
response = TestBattal.http.post('kill',data=payload)
self.assertEqual(200, response.status_code, '请求返回非200')
self.assertIn('win', response.text, '响应不包含win')
if __name__ == '__main__':
unittest.main()
第二个例子
import random
import hmac
import hashlib
import json
import unittest
from test_project.common.http_requests import HttpRequests
class TestUserApi(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.url = 'http://127.0.0.1:5000/'
cls.http = HttpRequests(cls.url)
cls.device_sn = '123456789'
cls.os_platform = 'ios'
cls.app_version = '1.0'
cls.SECRET_KEY = "mikezhou"
cls.user_id = random.randint(10, 100) # 随机生成id
print(cls.user_id)
@staticmethod
def get_token():
'''获取token'''
uri = '/api/get-token'
headers = {'device_sn': TestUserApi.device_sn,
'os_platform': TestUserApi.os_platform,
'app_version': TestUserApi.app_version,
'Content-Type': 'application/json'}
args = (TestUserApi.device_sn, TestUserApi.os_platform, TestUserApi.app_version)
content = ''.join(args).encode('ascii')
sign_key = TestUserApi.SECRET_KEY.encode('ascii')
sign = hmac.new(sign_key, content, hashlib.sha1).hexdigest()
data = {'sign': sign}
response = TestUserApi.http.post(uri, data=json.dumps(data), headers=headers)
print(response.text)
token = response.json().get('token')
print(token)
return token
def setUp(self) -> None:
self.headers = {'device_sn': TestUserApi.device_sn,
'token': TestUserApi.get_token(), # 调用静态方法获取token
'Content-Type': 'application/json'}
self.playload = {'name': 'mikezhou'}
def test_001_createUser(self):
'''测试创建用户'''
uri = '/api/users/{}'.format(TestUserApi.user_id) # format方法组装数据
response = TestUserApi.http.post(uri, data=json.dumps(self.playload), headers=self.headers)
print(response.text)
self.assertEqual(response.status_code, 201, '请求返回非201')
def test_002_query_users(self):
'''测试查询用户'''
uri = '/api/users/{}'.format(TestUserApi.user_id)
response = TestUserApi.http.get(uri, data=json.dumps(self.playload), headers=self.headers)
print(response.text)
self.assertEqual(response.status_code, 200, '请求返回非200')
self.assertIn(json.dumps(self.playload),response.text)
def test_003_query_all_users(self):
'''测试查询所有用户'''
uri = '/api/users'
response = TestUserApi.http.get(uri, data=json.dumps(self.playload), headers=self.headers)
print(response.text)
count = response.json().get('count')
items = response.json().get('items')
self.assertEqual(response.status_code, 200, '请求返回非200')
self.assertEqual(count,len(items)) # 判断数量正确
def test_004_update_users(self):
'''测试更新用户'''
uri = '/api/users/{}'.format(TestUserApi.user_id)
self.playload = {'name': 'mikezhou_{}'.format(random.randint(1,10))}
response = TestUserApi.http.put(uri, data=json.dumps(self.playload), headers=self.headers)
print(response.text)
self.assertEqual(response.status_code, 200, '请求返回非200')
self.assertIn(json.dumps(self.playload),response.text)
def test_005_delete_users(self):
'''测试删除用户'''
uri = '/api/users/{}'.format(TestUserApi.user_id)
self.playload = {'name': 'mikezhou_{}'.format(random.randint(1,10))}
response = TestUserApi.http.delete(uri, data=json.dumps(self.playload), headers=self.headers)
print(response.text)
self.assertEqual(response.status_code, 200, '请求返回非200')
if __name__ == '__main__':
unittest.main()
创建按类执行测试用例的执行策略
'''
按指定类运行测试用例
'''
import unittest
from test_project.test_case.test_battal import TestBattal
if __name__ == '__main__':
# 根据给定的测试类,获取其中所有以test开头的测试方法,并返回一个测试套件
suite1 = unittest.TestLoader().loadTestsFromTestCase(TestBattal)
# 将多个测试类加载到测试套件中
suite = unittest.TestSuite([suite1])
# 设置verbosity = 2,可以打印出更详细的执行信息
unittest.TextTestRunner(verbosity=2).run(suite)
输出测试报告
下载类库
如果想要生成 HTML 格式的报告,那么就需要额外借助第三方库(如 HtmlTestRunner)来操作。HTMLTestRunner是一个第三方的unittest HTML报告库,首先我们下载HTMLTestRunner.py,并放到对应目录下。
官方原版:http://tungwaiyip.info/software/HTMLTestRunner.html
GitHub地址:https://github.com/SeldomQA/HTMLTestRunner
整合到自己的框架中
放到lib目录下
创建一个新的执行策略
HTMLTestRunner类说明:
◆ stream : 指定报告的路径
◆ title : 报告的标题
◆ description : 报告的描述
run()方法说明:
◆ suit : 运行的测试套件
◆ rerun :重跑次数
◆ save_last_run :是否保存最后一个结果
测试框架支持ws协议
首先要安装websocket类库,在common目录下新建ws_websocket.py类
ddt数据驱动
参数化测试是一种“数据驱动测试”(Data-Driven Test),在同一个方法上测试不同的参数,以覆盖所有可能的预期分支的结果。它的测试数据可以与测试行为分离,被放入到文件、数据库或者外部介质中,再由测试程序读取。
Python 标准库中的unittest 自身不支持参数化测试,为了解决这个问题,有人专门开发了两个库:一个是ddt ,一个是parameterized 。
Python可以使用xlrd库来读取Excel文件(对xls,xlsx格式文件都可以读取),使用xlwt库来生成Excel文件(只支持到excel 2003,即xls文件),另外openpyxl库同时支持读、写excel文件,且主要针对Excel2007之
后的版本(.xlsx)。
ddt例子
import ddt
import unittest
@ddt.ddt
class MyTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
@ddt.data([1, 2, 3, 6], [2, 3, 4, 9], [3, 4, 5, 12])
# @ddt.data([1,2,3,6])
@ddt.unpack
def test_add(self, testdata1, testdata2, testdate3, exceptdata):
sum = testdata1 + testdata2 + testdate3
self.assertEqual(sum, exceptdata)
if __name__ == '__main__':
unittest.main()
ddt数据文件例子
import ddt
import unittest
@ddt.ddt
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(self):
pass
@classmethod
def tearDownClass(self):
pass
def setUp(self):
pass
def tearDown(self):
pass
@ddt.file_data('data.json')
@ddt.unpack
def test_sum(self, value):
a, b, sum = value.split('||')
print(a, b, sum)
result = int(a) + int(b)
self.assertEqual(result, int(sum))
if __name__ == '__main__':
unittest.main()
data.json
[
"1||2||3",
"3||2||5",
"3||5||8"
]
在框架中集成数据驱动能力
在common中创建parse_excel.py类,此类可以从excel中提取数据
from openpyxl import load_workbook
class ParseExcel(object):
def __init__(self, excelPath, sheetName):
print(excelPath,sheetName)
self.wb = load_workbook(excelPath)
self.sheet = self.wb[sheetName]
self.maxRowNum = self.sheet.max_row
def getDatasFromSheet(self):
dataList = []
for line in self.sheet.rows:
tmpList=[]
tmpList.append(line[0].value)
tmpList.append(line[1].value)
dataList.append(tmpList)
return dataList[2:]
在test_data目录下存放测试数据的excel文件
使用ddt实现数据驱动测试
import os
import random
import hmac
import hashlib
import json
import unittest
import ddt
from test_project.common.http_requests import HttpRequests
from test_project.common.parse_excel import ParseExcel
def get_test_data():
'''
从外部获取参数数据
:return:
'''
path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'test_data')
excelPath = os.path.join(path, 'test_user_api_data.xlsx')
print(excelPath)
sheetName = '用户参数表'
return ParseExcel(excelPath, sheetName)
@ddt.ddt
class TestUserApiByTDD(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.url = 'http://127.0.0.1:5000/'
cls.http = HttpRequests(cls.url)
cls.device_sn = '123456789'
cls.os_platform = 'ios'
cls.app_version = '1.0'
cls.SECRET_KEY = "mikezhou"
cls.user_id = random.randint(10, 100)
print(cls.user_id)
@ddt.data(*get_test_data().getDatasFromSheet())
def test_001_createUser(self, data):
'''测试创建用户'''
users, exp = tuple(data)
print(users,exp)
uri = '/api/users/{}'.format(users)
response = TestUserApiByTDD.http.post(uri, data=json.dumps(self.playload), headers=self.headers)
print(response.text)
self.assertEqual(response.status_code, 201, '请求返回非201')
self.assertIn(exp, response.text)
if __name__ == '__main__':
unittest.main()
mock
mock简单例子
- 例子1
import requests
from unittest import mock
def send_url():
url = 'http://127.0.0.1:51000/'
return requests.get(url)
# response = send_url()
# print(response)
sendUrl = mock.Mock(return_value="hello world")
response = sendUrl()
print(response)
sendUrl = mock.Mock(return_value={"code": 0, "msg": "登陆成功"})
response = sendUrl()
print(response)
sendUrl = mock.Mock(side_effect=ConnectionError('URL地址不通'))
response = sendUrl()
- 例子2
from unittest import mock
import unittest
from count import Count
class MockDemo(unittest.TestCase):
def test_001_add(self):
count = Count()
count.add = mock.Mock(return_value=13)
result = count.add(8, 8)
print(result)
self.assertEqual(result, 16)
def test_002_add(self):
count = Count()
# 如果return_value和side_effect同时存在,则side_effect会覆盖return_value
count.add = mock.Mock(return_value=13,side_effect=count.add)
result = count.add(8, 8)
print(result)
self.assertEqual(result, 16)
if __name__ == '__main__':
unittest.main()
class Count():
def add(self, a, b):
return a + b
- 例子3, mock patch使用示例
通过装饰器mock方法,通过装饰器mock比前面直接创建mock类更常用。
# pay.py
def zhifu():
'''假设这里是一个支付的功能,未开发完
支付成功返回:{"result": "success", "msg":"支付成功"}
支付失败返回:{"result": "fail", "msg":"余额不足"}
'''
pass
def zhifu_statues():
'''根据支付的结果success or fail,判断跳转到对应页面'''
result = zhifu()
try:
if result["result"] == "success":
return "支付成功"
elif result["result"] == "fail":
return "支付失败"
else:
return "未知错误异常"
except:
return "Error, 服务端返回异常!"
import unittest
from unittest import mock
import pay
class TestZhifuStatues(unittest.TestCase):
'''单元测试用例'''
@mock.patch("pay.zhifu")
def test_01(self, mock_zhifu):
'''测试支付成功场景'''
# 方法一:mock一个支付成功的数据
pay.zhifu = mock.Mock(return_value={"result": "success", "msg":"支付成功"})
print(pay.zhifu())
# 方法二:mock.patch装饰器模拟返回结果
mock_zhifu.return_value = {"result": "success", "msg":"支付成功"}
# # 根据支付结果测试页面跳转
statues = pay.zhifu_statues()
print(statues)
self.assertEqual(statues, "支付成功")
if __name__ == "__main__":
unittest.main()
- mock 类中的方法
前面是直接mock模块中的方法,现在讲下如果mock模块中的类的方法
# pay_class.py
class Zhifu():
def zhifu(self):
'''假设这里是一个支付的功能,未开发完
支付成功返回:{"result": "success", "reason":"null"}
支付失败返回:{"result": "fail", "reason":"余额不足"}
reason返回失败原因
'''
pass
class Statues():
def zhifu_statues(self):
'''根据支付的结果success or fail,判断跳转到对应页面'''
result = Zhifu().zhifu()
try:
if result["result"] == "success":
return "支付成功"
elif result["result"] == "fail":
return "支付失败"
else:
return "未知错误异常"
except:
return "Error, 服务端返回异常!"
import unittest
from unittest import mock
from pay_class import Zhifu,Statues
class Test_zhifu_statues(unittest.TestCase):
'''单元测试用例'''
@mock.patch("pay_class.Zhifu")
def test_01(self, mock_Zhifu):
'''测试支付成功场景'''
# 先模拟类,再模拟类的中方法
pay_class = mock_Zhifu.return_value # 先返回实例,对类名称替换
# 通过实例调用方法,再对方法的返回值替换
pay_class.zhifu.return_value = {"result": "success", "msg":"支付成功"}
print(pay_class.zhifu())
# 根据支付结果测试页面跳转
statues = Statues().zhifu_statues()
print(statues)
self.assertEqual(statues, "支付成功")
@mock.patch("pay_class.Zhifu.zhifu")
def test_02(self, mock_zhifu):
'''测试支付失败场景'''
# 直接模拟类中的方法
mock_zhifu.return_value = {"result": "fail", "msg": "余额不足"}
print(mock_zhifu())
# 根据支付结果测试页面跳转
statues = Statues().zhifu_statues()
print(statues)
self.assertEqual(statues, "支付失败")
@unittest.mock.patch.object(Zhifu, 'zhifu')
def test_03(self, mock_obj):
'''测试支付成功场景,另外一种Mock方式'''
mock_obj.return_value = {"result": "success", "reason": "Mock成功了,欢呼吧!"}
statues = Statues().zhifu_statues()
print(statues)
self.assertEqual(statues, "支付成功")
def test_04(self):
'''测试支付成功场景,最后一种Mock写法'''
with unittest.mock.patch.object(Zhifu, 'zhifu') as mock_obj:
mock_obj.return_value = {"result": "success", "reason": "Mock成功了,欢呼吧!"}
statues = Statues().zhifu_statues()
print(statues)
self.assertEqual(statues, "支付成功")
if __name__ == "__main__":
unittest.main()
mock side_effect使用示例
import unittest
import unittest.mock
class MyTest(unittest.TestCase):
def test_except(self):
# 1. 创建Mock()对象,传递异常对象
mock_obj = unittest.mock.Mock(side_effect=BaseException('自定义异常'))
# mock_obj是对象,可调用对象,用法和函数一样
mock_obj()
def test_list(self):
# 1. 创建Mock()对象,传递list
mock_obj = unittest.mock.Mock(side_effect=[1,2,3])
# mock_obj是对象,可调用对象,用法和函数一样
print(mock_obj())
print(mock_obj())
print(mock_obj())
def test_func(self):
def func(a, b):
return a+b
# 1. 创建Mock()对象,传递函数名
mock_obj = unittest.mock.Mock(side_effect=func)
# mock_obj是对象,可调用对象,用法和函数一样
print(mock_obj(2, 3))
if __name__ == '__main__':
unittest.main()