1)环境准备:html
接口测试的方式有不少,好比能够用工具(jmeter,postman)之类,也能够本身写代码进行接口测试,工具的使用相对来讲都比较简单,重点是要搞清楚项目接口的协议是什么,而后有针对性的进行选择,甚至当工具不太适合项目时须要本身进行开发。python
在咱们项目的初期,咱们采用的是jmeter进行接口测试,当时以为这个工具上手简单,团队成员学习成本低,而且接口测试的脚本稍微调整一下还能够用来作性能测试。程序员
不过随着项目规模、团队人数的不断增加,渐渐的这个工具备适应不了当前项目的需求了,为此咱们项目也从新开发了相关接口自动化的平台。可是,可是。。。多是我让你们中毒太深,如今不少同窗一提到接口测试关联到jmeter,为此,我深深感到不安。毕竟jmeter只是个工具,换个项目换个协议你是否还能玩转接口测试呢?session和cookie有什么区别?工具又是怎么实现的呢?面试
好比session如何保存,接口依赖如何处理,case如何管理及执行顺序,测试数据如何管理等等题,这个过程也有助于咱们更加深入的理解接口测试和http协议。json
本文主要采用python语言,python中http协议接口相关的库有urllib,urllib2以及reqeusts库,这其中reqeusts库用来起来最方便,所以我也主要采用requests库来作http协议的接口测试。首先来看下须要哪些环境信息:python3.x
1、安装python服务器
安装Python这个就很少说了。微信
2、安装虚拟环境:cookie
咱们在一台机器上能够安装多个python版本,为了使每一个版本的环境相互不受干扰,能够安装虚拟环境,安装方法以下:网络
一、安装virtualenv:pip install virtualenv
二、新建名为venv的虚拟环境:virtualenv venv
三、进入新环境:source venv/bin/activate
四、退出:deactivate
3、安装requests库:
pip install requests
ps:用python作http协议的接口测试会用到这个库。
若是对软件测试有兴趣,想了解更多的测试知识,解决测试问题,以及入门指导,帮你解决测试中遇到的困惑,咱们这里有技术高手。若是你正在找工做或者刚刚学校出来,又或者已经工做可是常常以为难点不少,以为本身测试方面学的不够精想要继续学习的,想转行怕学不会的, 均可以加入咱们1079636098,群内可领取最新软件测试大厂面试资料和Python自动化、接口、框架搭建学习资料!
4、http测试工具:
一个使用 Python + Flask 编写的 HTTP 请求和响应服务,该服务主要用于测试 HTTP 库。后续测试咱们都基于这个网站。
5、在本地搭建httpbin:
考虑到测试时要不断访问 httpbin 网站,请求过多担忧被拉到黑名单,咱们本身在本志搭建一套httpbin服务。
一、安装:pip install gunicorn
二、安装:pip install httpbin
三、启动:gunicorn httpbin:app
至此,环境搭建已经完毕,能够开始玩了~
(2)requests.get()
环境搭建好后,接下来咱们先来了解一下requests的一些简单使用,主要包括:
requests经常使用请求方法使用,包括:get,postrequests库中的Session、Cookie的使用其它高级部分:认证、代理、证书验证、超时配置、错误异常处理等。
本节首先来了解一下requests库中如何发送get请求:
1、看下方法定义:
一、到官方文档去了下requests.get()方法的定义,以下:

二、点击右上角的【source】,看一下它的源码以下:
看到最后一行return,get方法最后是经过调用 requests.request方法实现的,其实在其它的请求方法如post,put,head,delete等方法都是调用的request方法,而后把请求方法的类型传递给request方法第一个参数。
三、HTTP协议是一个基于请求/响应模式的、无状态的,应用层协议。既然有请求,就有响应,来看下resquest中经常使用的响应信息:

2、get方法简单使用:
一、不带参数的get:
# -*- coding:utf-8 -*-
#不带参数的get
import requests
import json
host = "/"
endpoint = "get"
url = ''.join([host,endpoint])
r = requests.get(url)
#response = r.json()
print type(r.text)
print (eval(r.text))
输出:
{'origin': '183.14.133.88','headers': {'Connection': 'close','Host': 'httpbin.org','Accept-Encoding': 'gzip,deflate','Accept': '*/*','User-Agent': 'python-requests/2.18.1'},'args': {},'url': 'http: //httpbin.org/get'}
二、 带参数的get:
# -*- coding:utf-8 -*-
#带参数的get
import requests
import json
host = "/"
endpoint = "get"
url = ''.join([host,endpoint])
params = {"show_env":"1"}
r = requests.get(url=url,params=params)
print r.url
输出:
/get?show_env=1
{
'origin': '183.14.133.88',
'headers': {
'X-Request-Id': 'ebe922b4-c463-4fe9-9faf-49748d682fd7',
'Accept-Encoding': 'gzip,
deflate',
'X-Forwarded-Port': '80',
'Total-Route-Time': '0',
'Connection': 'close',
'Connect-Time': '0',
'Via': '1.1vegur',
'X-Forwarded-For': '183.14.133.88',
'Accept': '*/*',
'User-Agent': 'python-requests/2.18.1',
'X-Request-Start': '1504755961007',
'Host': 'httpbin.org',
'X-Forwarded-Proto': 'http'
},
'args': {
'show_env': '1'
},
'url': 'http: //httpbin.org/get?show_env=1'
}
三、带header的get:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "get"
url = ''.join([host,endpoint])
headers = {"User-Agent":"test request headers"}
r = requests.get(url)
r = requests.get(url,headers=headers)
#response = r.json()
print (eval(r.text))['headers']['User-Agent']
输出:
test request headers
四、同时带参数和header:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "get"
url = ''.join([host,endpoint])
headers = {"User-Agent":"test request headers"}
params = {"show_env":"1"}
r = requests.get(url)
r = requests.get(url,headers=headers,params=params)
#response = r.json()
print (eval(r.text))['headers']['User-Agent']
print r.url
输出:
test request headers
/get?show_env=1
(3)requests.post()
1、方法定义
2、post方法简单使用
一、带数据的post
二、带header的post
三、带json的post
四、带参数的post
五、普通文件上传
六、定制化文件上传
七、多文件上传
1、方法定义:
一、到官方文档去了下requests.post()方法的定义,以下:

二、源码:

三、经常使用返回信息:

2、post方法简单使用:
一、带数据的post:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
data = {'key1':'value1','key2':'value2'}
r = requests.post(url,data=data)
#response = r.json()
print (r.text)
输出:
{
"args": {},
"data": "",
"files": {},
"form": {
"key1": "value1",
"key2": "value2"
},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.1"
},
"json": null,
"origin": "183.14.133.88",
"url": "/post"
}
二、带header的post:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
headers = {"User-Agent":"test request headers"}
# r = requests.post(url)
r = requests.post(url,headers=headers)
#response = r.json()
输出:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "0",
"Host": "httpbin.org",
"User-Agent": "test request headers"
},
"json": null,
"origin": "183.14.133.88",
"url": "/post"
}
三、带json的post:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
data = {
"sites": [
{ "name":"test" , "url":"www.test.com" },
{ "name":"google" , "url":"www.google.com" },
{ "name":"weibo" , "url":"www.weibo.com" }
]
}
r = requests.post(url,json=data)
# r = requests.post(url,data=json.dumps(data))
response = r.json()
输出:
{
"args": {},
"data": "{\"sites\": [{\"url\": \"www.test.com\", \"name\": \"test\"}, {\"url\": \"www.google.com\", \"name\": \"google\"}, {\"url\": \"www.weibo.com\", \"name\": \"weibo\"}]}",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "140",
"Content-Type": "application/json",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.1"
},
"json": {
"sites": [
{
"name": "test",
"url": "www.test.com"
},
{
"name": "google",
"url": "www.google.com"
},
{
"name": "weibo",
"url": "www.weibo.com"
}
]
},
"origin": "183.14.133.88",
"url": "/post"
}
四、带参数的post:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
params = {'key1':'params1','key2':'params2'}
# r = requests.post(url)
r = requests.post(url,params=params)
#response = r.json()
print (r.text)
输出:
{
"args": {
"key1": "params1",
"key2": "params2"
},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "0",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.1"
},
"json": null,
"origin": "183.14.133.88",
"url": "/post?key2=params2&key1=params1"
}
5.普通文件上传:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
#普通上传
files = {
'file':open('test.txt','rb')
}
r = requests.post(url,files=files)
print (r.text)
输出:
{
"args": {},
"data": "",
"files": {
"file": "hello world!\n"
},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "157",
"Content-Type": "multipart/form-data; boundary=392865f79bf6431f8a53c9d56c62571e",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.1"
},
"json": null,
"origin": "183.14.133.88",
"url": "/post"
}
6.定制化文件上传:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
#自定义文件名,文件类型、请求头
files = {
'file':('test.png',open('test.png','rb'),'image/png')
}
r = requests.post(url,files=files)
print (r.text)heman793
7.多文件上传:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
#多文件上传
files = [
('file1',('test.txt',open('test.txt', 'rb'))),
('file2', ('test.png', open('test.png', 'rb')))
]
r = requests.post(url,files=files)
print (r.text)
8.流式上传:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "post"
url = ''.join([host,endpoint])
#流式上传
with open( 'test.txt' ) as f:
r = requests.post(url,data = f)
print (r.text)
输出:
{
"args": {},
"data": "hello world!\n",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Content-Length": "13",
"Host": "httpbin.org",
"User-Agent": "python-requests/2.18.1"
},
"json": null,
"origin": "183.14.133.88",
"url": "/post"
}
(4)Cookie&Session
掌握了前面几节的的内容,就能够作一些简单的http协议接口的请求发送了,可是这些还不够。HTTP协议是一个无状态的应用层协议,也就是说先后两次请求是没有任何关系的,那若是咱们测试的接口以前有相互依赖关系怎么办呢(好比我要在博客园发文章,是须要先登陆的),这时咱们就要用到cookie和session技术来保持客户端与服务器端链接的状态,这也就是本节要介绍的内容:
1、Cookie:
一、获取cookie:
# -*- coding:utf-8 -*-
#获取cookie
import requests
import json
url = ""
r = requests.get(url)
#将RequestsCookieJar转换成字典
c = requests.utils.dict_from_cookiejar(r.cookies)
print r.cookies
print c
for a in r.cookies:
print a.name,a.value
输出:
]>
{'BDORZ': '27315'}
BDORZ 27315
二、发送Cookie
# -*- coding:utf-8 -*-
#发送cookie到服务器
import requests
import json
host = "/"
endpoint = "cookies"
url = ''.join([host,endpoint])
#方法一:简单发送
# cookies = {"aaa":"bbb"}
# r = requests.get(url,cookies=cookies)
# print r.text
#方法二:复杂发送
s = requests.session()
c = requests.cookies.RequestsCookieJar()
c.set('c-name','c-value',path='/xxx/uuu',domain='.test.com')
s.cookies.update(c)
2、Session
一、保持会话同步:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "cookies"
url = ''.join([host,endpoint])
url1 = "/cookies/set/sessioncookie/123456789"
r = requests.get(url)
print r.text
print "------"
s = requests.session() #初始化一个session对象
s.get(url1) #cookie的信息存在了session中
r = s.get(url)
print r.text
输出:
{
"cookies": {}
}
------
{
"cookies": {
"sessioncookie": "123456789"
}
}
二、保存绘画信息:
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "headers"
url = ''.join([host,endpoint])
header1 = {"testA":"AAA"}
header2 = {"testB":"BBB"}
s = requests.session() #初始化一个session对象
s.headers.update(header1) #已经存在于服务中的信息
r = s.get(url,headers=header2) #发送新的信息
print r.text
输出:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"Testa": "AAA",
"Testb": "BBB",
"User-Agent": "python-requests/2.18.1"
}
}
3.删除已存在的会话信息,保存为None
# -*- coding:utf-8 -*-
import requests
import json
host = "/"
endpoint = "headers"
url = ''.join([host,endpoint])
header1 = {"testA":"AAA"}
header2 = {"testB":"BBB"}
s = requests.session() #初始化一个session对象
s.headers.update(header1) #已经存在于服务中的信息
r = s.get(url,headers=header2) #发送新的信息
print r.text
print '--------'
s.headers['testA'] = None #删除会话里的信息testA
r1 = s.get(url,headers = header2)
print r1.text
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"Testa": "AAA",
"Testb": "BBB",
"User-Agent": "python-requests/2.18.1"
}
}
--------
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"Testb": "BBB",
"User-Agent": "python-requests/2.18.1"
}
}
四、提供默认数据:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"Testa": "AAA",
"Testb": "BBB",
"User-Agent": "python-requests/2.18.1"
}
}
--------
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Connection": "close",
"Host": "httpbin.org",
"Testb": "BBB",
"User-Agent": "python-requests/2.18.1"
}
}
参考:
#cookies
#session-objects
(5)其余(认证&代理&超时设置)
1、认证
一、基本认证:
# -*- coding:utf-8 -*-
import requests
url = "/basic-auth/user/passwd"
r1 = requests.get(url)
print "未提供用户名密码:" + str(r1.status_code)
#Basic Authentication
r2 = requests.get(url,auth=('user','passwd'))
print "已提供用户名密码:" + str(r2.status_code)
输出:
未提供用户名密码:401
已提供用户名密码:200
二、数字认证:
>>> from requests.auth import HTTPDigestAuth>>> url = '/digest-auth/auth/user/pass'>>> requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
三、OAuth认证:
参考:
2、代理
一、方法一:proxy参数:
import requests
proxies = {
"https": ""
}
r = requests.post("/post", proxies=proxies)
print r.text
二、方法二:设置环境变量:
$ export HTTP_PROXY=""
$ export HTTPS_PROXY=""
$ python
>>> import requests
>>> requests.get('')
三、HTTP Basic Auth使用代理方法:http://user:password@host/
proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}
3、证书验证
一、SSL证书(HTTPS):
import requests
#跳过12306 的证书验证,把 verify 设置为 False:
r = requests.get('', verify=False)
print r.text
二、客户端证书:
>>> requests.get('', cert=('/path/client.cert', '/path/client.key'))
or
s = requests.Session()
s.cert = '/path/client.cert'
4、超时配置
1 、利用timeout参数来配置最大请求时间:
r = requests.get('', timeout=5)
二、设置timeout=None,告诉请求永远等待响应,而不将请求做为超时值传递
r = requests.get('', timeout=None)
5、错误异常:
一、全部Requests显式抛出的异常都继承自:requests.exctptions.RequestException
二、遇到网络问题(如:DNS查询失败,拒绝链接等)时,requests会抛出一个ConnectionError异常
三、遇到罕见的无效HTTP响应时,Request则会抛出一个HTTPError异常
四、若请求超时,则抛出一个Timeout异常
五、若请求超过了最大的重写向次数,则会抛出一个TooManyRedirects异常
(6)unittest-单个用例管理:
上面主要介绍了环境搭建和requests库的使用,可使用这些进行接口请求的发送。可是如何管理接口案例?返回结果如何自动校验?这些内容光靠上面五节是不行的,所以从本节开始咱们引入python单元测试框架 unittest,用它来处理批量用例管理,校验返回结果,初始化工做以及测试完成后的环境复原工做等等。
1、单个用例管理起来比较简单,参考以下图,单个用例通常多用在调试的时候:

2、代码以下:
# -*- coding:utf-8 -*-
# 单个用例执行
# 一、导入模块
import unittest
# 二、继承自unittest.TestCase类
class TestOne(unittest.TestCase):
# 三、配置环境:进行测试前的初始化工做
def setUp(self):
print '\ncases before'
pass
# 四、定义测试用例,名字以“test”开头
def test_add(self):
'''test add method'''
print 'add...'
a = 3 + 4
b = 7
# 五、定义assert断言,判断测试结果
self.assertEqual(a, b)
def test_sub(self):
'''test sub method'''
print 'sub...'
a = 10 - 5
b = 4
self.assertEqual(a, b)
# 六、清理环境
def tearDown(self):
print 'case after'
pass
# 七、该方法会搜索该模块下全部以test开头的测试用例方法,并自动执行它们
if __name__ == '__main__':
unittest.main()
输出:
Ran 2 tests in 0.001s
OK
cases before
add...
case after
cases before
sub...
case after
Process finished with exit code 0
(8)unittest-生成测试报告:
用例的管理问题解决了后,接下来要考虑的就是报告我问题了,这里生成测试报告主要用到HTMLTestRunner.py 这个模块,下面简单介绍一下如何使用:
1、下载HTMLTestRunner下载:
这个模块不能经过pip安装,只能下载安装,下载地址以下:
python3.x版本:
2、mac下配置:
一、终端进入python环境
二、输入:
import sys
print sys.path
三、找到site-packages文件夹的路径并将下载的HTMLTestRunner.py文件拷贝到此的文件夹下
四、在python环境下,输入 import HTMLTestRunner 不报错即安装成功
3、使用该模块生成报告:
一、目录结构
case包下面有baidu,httpbin两个包
每一个包下面分别有两个测试的py文件
每一个test_00x.py文件里各有2个test case
run_all_case.py文件:用来执行全部的test case且生成测试报告

二、运行后生成报告以下:

三、run_all_case.py代码以下:
# -*- coding:utf-8 -*-
import unittest
import os
import time
import HTMLTestRunner
# 用例路径
case_path = os.path.join(os.getcwd())
# 报告存放路径
report_path = os.path.join(os.getcwd(), 'report')
print report_path
def all_case():
discover = unittest.defaultTestLoader.discover(case_path, pattern="test*.py", top_level_dir=None)
print discover
return discover
if __name__ == '__main__':
# 一、获取当前时间,这样便于下面的使用。
now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
# 二、html报告文件路径
report_abspath = os.path.join(report_path, "result_"+now+".html")
# 三、打开一个文件,将result写入此file中
fp = open(report_abspath, "wb")
runner = HTMLTestRunner.HTMLTestRunner(stream=fp,
title=u'接口自动化测试报告,测试结果以下:',
description=u'用例执行状况:')
# 四、调用add_case函数返回值
runner.run(all_case())
fp.close()
最后:
将来的你确定会感谢如今拼命的本身!
给你们推荐一个软件测试技术交流群:1079636098 群友福利免费领取
愿你我相遇,皆有所获! 欢迎关注微信公众号:程序员一凡
1.免费领取一份216页软件测试工程师面试宝典文档资料。
2.软件测试学习路线以及相对应的视频学习教程免费分享!