Python接口自动化测试实战:从环境搭建到用例管理

半杯拿铁
半杯拿铁
发布时间:2026-10-14 | 4人浏览过
原创

本文详细介绍基于Python的接口自动化测试实现方案。内容涵盖从环境搭建、requests库核心方法(GET/POST)到Cookie与Session状态保持,深入讲解认证、代理及异常处理机制,并结合unittest框架实现批量用例管理与HTMLTestRunner测试报告生成,帮助开发者构建完整的接口测试体系。

环境准备与工具选型

环境准备阶段

接口测试可通过JMeter、Postman等工具完成,也可通过编写代码实现。工具使用相对简单,关键在于明确项目接口的协议类型,据此选择合适的测试方式;当现有工具无法满足需求时,需自行开发测试脚本。

在项目初期,团队选用JMeter进行接口测试,主要因其上手简单、学习成本低,且脚本经调整后还可用于性能测试。

随着项目规模扩大和团队人员增加,JMeter逐渐难以适应当前需求,因此项目组重新开发了接口自动化平台。然而,许多团队成员对JMeter依赖较深,一提到接口测试便联想到该工具。这引发了对底层原理的思考:更换项目或协议后是否仍能熟练进行接口测试?Session与Cookie的区别是什么?工具背后的实现机制又是什么?

例如,Session的保存方式、接口依赖的处理、用例的管理与执行顺序、测试数据的管理等问题,深入探究这些环节有助于更深刻地理解接口测试原理及HTTP协议。

本文主要使用Python语言,Python中处理HTTP协议接口的库包括urllib、urllib2以及requests。其中,requests库使用最为便捷,因此本文主要基于requests库进行HTTP协议接口测试。首先明确所需的环境信息:

一、安装Python

Mac系统默认已预装Python,此处不再赘述。

二、安装虚拟环境

在一台机器上可安装多个Python版本,为避免各版本环境相互干扰,建议安装虚拟环境。具体安装方法如下:

1、安装virtualenv:pip install virtualenv

2、新建名为venv的虚拟环境:virtualenv venv

3、进入新环境:source venv/bin/activate

4、退出虚拟环境:deactivate

三、安装requests库


>>>pip install requests

注:使用Python进行HTTP协议接口测试需依赖此库。

四、HTTP测试工具

使用Python + Flask编写的HTTP请求和响应服务,主要用于测试HTTP库。后续测试均基于该网站进行。

http://httpbin.org

五、本地搭建httpbin服务

考虑到测试过程中频繁访问httpbin网站可能导致被拉入黑名单,建议在本地搭建一套httpbin服务。

1、安装:pip install gunicorn

2、安装:pip install httpbin

3、启动:gunicorn httpbin:app

至此,环境搭建已完成,可以开始进行测试实践。

requests库GET请求详解

(2)requests.get()方法

环境搭建完成后,接下来介绍requests库的一些基本用法,主要包括:

requests常用请求方法的使用,包括get、post;requests库中Session、Cookie的使用;其他高级功能:认证、代理、证书验证、超时配置、错误异常处理等。

本节首先介绍requests库中如何发送get请求:

一、查看方法定义

1、查阅官方文档中requests.get()方法的定义,如下所示:

2、点击右上角的【source】,查看其源码如下:

从最后一行return可见,get方法最终通过调用requests.request方法实现。实际上,post、put、head、delete等其他请求方法也是调用request方法,并将请求方法类型作为第一个参数传递给request方法。

3、HTTP协议是一种基于请求/响应模式的无状态应用层协议。既然有请求,就有响应,以下来看request中常用的响应信息:

二、get方法简单使用

1、不带参数的get请求:


# -*- coding:utf-8 -*-
#不带参数的get

import requests
import json

host = "http://httpbin.org/"
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'
}

2、带参数的get请求:


# -*- coding:utf-8 -*-
#带参数的get

import requests
import json

host = "http://httpbin.org/"
endpoint = "get"

url = ''.join([host,endpoint])
params = {"show_env":"1"}
r = requests.get(url=url,params=params)

print r.url

输出结果:


http://httpbin.org/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'
}

3、带header的get请求:


# -*- coding:utf-8 -*-

import requests
import json

host = "http://httpbin.org/"
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

4、同时带参数和header的get请求:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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
http://httpbin.org/get?show_env=1

requests库POST请求详解

(3)requests.post()方法

一、方法定义

二、post方法简单使用

1、带数据的post请求

2、带header的post请求

3、带json的post请求

4、带参数的post请求

5、普通文件上传

6、定制化文件上传

7、多文件上传

一、方法定义

1、查阅官方文档中requests.post()方法的定义,如下所示:

2、查看源码

3、常用返回信息

二、post方法简单使用

1、带数据的post请求:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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": "http://httpbin.org/post"
}

2、带header的post请求:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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": "http://httpbin.org/post"
}

3、带json的post请求:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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": "http://httpbin.org/post"
}

4、带参数的post请求:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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": "http://httpbin.org/post?key2=params2&key1=params1"
}

5、普通文件上传:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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": "http://httpbin.org/post"
}

6、定制化文件上传:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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 = "http://httpbin.org/"
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 = "http://httpbin.org/"
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": "http://httpbin.org/post"
}

Cookie与Session状态管理

(4)Cookie与Session机制

掌握前述内容后,即可发送简单的HTTP协议接口请求,但这尚不足够。HTTP协议是无状态的应用层协议,前后两次请求相互独立。若测试的接口存在依赖关系(例如在博客园发文章需先登录),则需使用Cookie和Session技术来保持客户端与服务器端连接的状态,这正是本节介绍的内容:

一、Cookie

1、获取Cookie:


# -*- coding:utf-8 -*-
#获取cookie
import requests
import json

url = "https://www.baidu.com/"
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

输出结果:


<RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
{'BDORZ': '27315'}
BDORZ 27315

2、发送Cookie


# -*- coding:utf-8 -*-
#发送cookie到服务器
import requests
import json

host = "http://httpbin.org/"
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)

二、Session

1、保持会话同步:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
endpoint = "cookies"

url = ''.join([host,endpoint])
url1 = "http://httpbin.org/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"
 }
}

2、保存会话信息:


# -*- coding:utf-8 -*-
import requests
import json

host = "http://httpbin.org/"
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 = "http://httpbin.org/"
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"
 }
}

4、提供默认数据:


s = requests.Session()
s.auth = ('user', 'pass')
s.headers.update({'x-test': 'true'})

# both 'x-test' and 'x-test2' are sent
s.get('http://httpbin.org/headers', headers={'x-test2': 'true'})

参考文档:

http://docs.python-requests.org/en/master/user/quickstart/#cookies http://docs.python-requests.org/en/master/user/advanced/#session-objects

高级功能:认证、代理与异常处理

(5)其他功能(认证、代理、超时设置)

一、认证

1、基本认证:


# -*- coding:utf-8 -*-
import requests

url = "http://httpbin.org/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。

2、数字认证


>>> from requests.auth import HTTPDigestAuth
>>> url = 'http://httpbin.org/digest-auth/auth/user/pass'
>>> requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
<Response [200]>

3、OAuth认证

参考文档:http://docs.python-requests.org/en/master/user/authentication/

二、代理

1、方法一:使用proxy参数:


import requests

proxies = {
 "https": "http://41.118.132.69:4433"
}
r = requests.post("http://httpbin.org/post", proxies=proxies)
print r.text

2、方法二:设置环境变量:


$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"

$ python
>>> import requests
>>> requests.get('http://example.org')

3、HTTP Basic Auth使用代理方法:http://user:password@host/


proxies = {'http': 'http://user:pass@10.10.1.10:3128/'}

三、证书验证

1、SSL证书(HTTPS):


import requests

#跳过12306 的证书验证,把 verify 设置为 False: 
r = requests.get('https://kyfw.12306.cn/otn/', verify=False)
print r.text

2、客户端证书:


>>> requests.get('https://kennethreitz.org', cert=('/path/client.cert', '/path/client.key'))
<Response [200]>

或者


s = requests.Session()
s.cert = '/path/client.cert'

四、超时配置

1、利用timeout参数配置最大请求时间:


r = requests.get('https://github.com', timeout=5)

2、设置timeout=None,表示请求永远等待响应,不将请求作为超时值传递


r = requests.get('https://github.com', timeout=None)

五、错误异常处理

1、所有Requests显式抛出的异常均继承自:requests.exctptions.RequestException

2、遇到网络问题(如DNS查询失败、拒绝连接等)时,requests会抛出ConnectionError异常

3、遇到罕见的无效HTTP响应时,Request会抛出HTTPError异常

4、若请求超时,则抛出Timeout异常

5、若请求超过最大重定向次数,则会抛出TooManyRedirects异常

unittest框架用例管理与报告生成

(6)unittest-单个用例管理

前文主要介绍了环境搭建和requests库的使用,可用于发送接口请求。但如何管理接口用例?如何自动校验返回结果?仅靠前几节内容无法实现,因此从本节开始引入Python单元测试框架unittest,用于处理批量用例管理、校验返回结果、初始化工作及测试完成后的环境复原等工作。

一、单个用例管理相对简单,参考下图,单个用例通常多用于调试场景:

二、代码如下:


# -*- coding:utf-8 -*-
# 单个用例执行
# 1、导入模块
import unittest

# 2、继承自unittest.TestCase类
class TestOne(unittest.TestCase):
  # 3、配置环境:进行测试前的初始化工作
  def setUp(self):
    print '\ncases before'
    pass

  # 4、定义测试用例,名字以“test”开头
  def test_add(self):
    '''test add method'''
    print 'add...'
    a = 3 + 4
    b = 7
    # 5、定义assert断言,判断测试结果
    self.assertEqual(a, b)

  def test_sub(self):
    '''test sub method'''
    print 'sub...'
    a = 10 - 5
    b = 4
    self.assertEqual(a, b)

  # 6、清理环境
  def tearDown(self):
    print 'case after'
    pass

# 7、该方法会搜索该模块下所有以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模块,下面简要介绍其使用方法:

一、下载HTMLTestRunner模块

该模块无法通过pip安装,需手动下载安装,下载地址如下:

python2.x版本:http://tungwaiyip.info/software/HTMLTestRunner.html

python3.x版本:http://hzqldjb.blog.51cto.com/9587820/1590802

二、Mac下配置方法

1、终端进入Python环境

2、输入:


import sys
print sys.path

3、找到site-packages文件夹路径,并将下载的HTMLTestRunner.py文件拷贝至该文件夹下

4、在Python环境下,输入import HTMLTestRunner,若不报错则表明安装成功

三、使用该模块生成报告

1、目录结构

  • case包下包含baidu、httpbin两个子包
  • 每个子包下分别有两个测试py文件
  • 每个test_00x.py文件中各有2个test case
  • run_all_case.py文件:用于执行所有test case且生成测试报告

2、运行后生成报告如下:

3、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__':
  # 1、获取当前时间,这样便于下面的使用。
  now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))

  # 2、html报告文件路径
  report_abspath = os.path.join(report_path, "result_"+now+".html")

  # 3、打开一个文件,将result写入此file中
  fp = open(report_abspath, "wb")
  runner = HTMLTestRunner.HTMLTestRunner(stream=fp,
                      title=u'接口自动化测试报告,测试结果如下:',
                      description=u'用例执行情况:')
  # 4、调用add_case函数返回值
  runner.run(all_case())
  fp.close()

到此,关于Python接口自动化测试的实现文章介绍完毕,更多相关Python接口自动化测试内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表