千家信息网

干货丨Python接口测试自动化实战及代码示例:含get、post等方法

发表于:2025-12-02 作者:千家信息网编辑
千家信息网最后更新 2025年12月02日,引言:年初参与到一个后台系统开发的项目中,里面涉及了很多接口,我做为项目组测试人员,需要对这些接口进行测试,一开始使用 postman 工具测试,很是方便。但随着接口数量的增加,不光要执行手动点击测试
千家信息网最后更新 2025年12月02日干货丨Python接口测试自动化实战及代码示例:含get、post等方法

引言:年初参与到一个后台系统开发的项目中,里面涉及了很多接口,我做为项目组测试人员,需要对这些接口进行测试,一开始使用 postman 工具测试,很是方便。但随着接口数量的增加,不光要执行手动点击测试,而且,一旦接口参数变动,都重新更改接口参数,次数多了,使得测试效率严重下降。

后来我将目光转向了自动化测试,考虑到项目组对接口质量要求很高,需要快速开发。最终选定 python 作为脚本开发语言,使用其自带的 requests 和 urllib 模块进行接口请求,使用优化后的 unittest 测试框架编写测试接口函数,测试结果选用 HTMLTestRunner 框架予以展示,并使用 python 的 ssl 模块支持 https 协议的验证。接下来,我详细地介绍这些模块,并给出各个模块完整的测试代码。

1、接口请求

python 特别是 python 3.x 中的 urllib 和 requests 模块,是用来请求 url 的两个主要模块。这两个模块中,如果仅仅是支持 http 协议的 url 请求,推荐使用 requests 模块。为什么这么说呢?因为爱因斯坦说过一句话:简洁就是美。requests 模块对 urllib 模块又做了一层封装,使用更加方便。该模块支持 GET, POST, PUT, DELETE 等请求方法。请求返回信息包含状态码和消息体,状态码用三位数字表示,消息体可用字符串,二进制或json 等格式表示。下面用一个例子来介绍一下 requests 模块的使用。代码如下:

import requests

def get_method(url, para, headers):

try:

req = requests.get(url=url, params=para, headers=headers)

except Exception as e:

print(e)

else:

if req.status_code == "200":

return req

else:

print("Requests Failed.")

if __name__=='__main__':

url = "http://www.google.com"

req = get_method(url=url, para=None, headers=None)

print(req.status_code)

print(req.text)

输出为:

200

上述程序输出状态码为 200,表明请求成功,返回消息体为网页内容。这里我仅对requests 模块中的 get 请求方法做了封装,其它方法(如 post,put,delete 等)的封装类似。当让你也可以不用封装,直接使用 requests.methodName 来直接调用该方法。这里提醒一句,在实际的接口测试中,headers 和 data 都是有值的,要确保这些值的填写正确,大部分请求下的请求失败或返回结果错误,基本上都是由于这些值的缺失或错误造成的。更多关于 requests 模块的介绍,请参考官方文档。

2、测试框架优化

unittest 是 python 中进行单元测试使用广泛的框架,其与 java 中的单元测试框架junit 类似。该框架使用简单,需要编写以 test 开头的函数,选择 unittest 框架运行测试函数,测试结果在终端显示。这里举一个简单的例子:

import unittest

class ApiTestSample(unittest.TestCase):

def setUp(self):

pass

def tearDown(self):

pass

def jiafa(self, input01, input02):

result = input01 + input02

return result

def test_jiafa(self):

testResult = self.jiafa(input01=4, input02=5)

self.assertEqual(testResult, 9)

if __name__=='__main__':

unittest.main()

简单解释下这段代码,首先我们创建一个类 ApiTestSample,这个类继承自unittest.TestCase 类。然后在这个类中写了 jiafa 函数,它有两个参数 input01,input02,返回 input01 与 input02 相加的和。接着在 test_jiafa 方法中,我们对刚才 jiafa 函数进行了和值校验。通过给 jiafa 输入两个值,获取其函数返回值,并与真实值做相等判断,以此实现函数单元测试。这里用到了 unittest 中断言值相等的 assertEqual(m, n)函数,上述代码运行结果如下:

Ran 1 test in 0.000s

OK

以上是 unittest 框架最基本的单元测试应用,但是这个框架有个缺陷,就是不能自己传入参数。对于接口来说,往往需要传入很多参数,并且这每个参数又有很多取值,如果不对原先的 unittest 框架做改变,不仅无法用来进行接口测试,而且一个个结合参数取值去写测试代码,工作量极其庞大,也没有实现测试数据与脚本没有分离。基于此,我们对该框架做出一下两点优化。

1)扩展 unittest.TestCase 类,支持自定义参数输入;

2)测试数据与测试脚本分离,测试数据存储在文件和数据库中,以增强测试脚本复用性;

以下是对 unittest.TestCase 类的扩展,使其支持参数化把参数加进去。下面是具体的代码实现过程:

class ExtendTestCaseParams(unittest.TestCase):

#扩展 unittest.TestCase 类,使其支持自定义参数输入

def __init__(self, method_name='runTest', canshu=None):

super(ExtendTestCaseParams, self).__init__(method_name)

self.canshu = canshu

#静态参数化方法

@staticmethod

def parametrize(testcase_klass, default_name=None, canshu=None):

""" Create a suite containing all tests taken from the given

subclass, passing them the parameter 'canshu'

"""

test_loader = unittest.TestLoader()

testcase_names = test_loader.getTestCaseNames(testcase_klass)

suite = unittest.TestSuite()

if default_name != None:

for casename in testcase_names:

if casename == defName:

suite.addTest(testcase_klass(casename, canshu=canshu))

else:

for casename in testcase_names:

suite.addTest(testcase_klass(casename, canshu=canshu))

return suite

这里,canshu 就是优化后加的自定义参数,参数类型可以是元组或列表。下面使用这个参数化类来改写之前的代码。

class ApiTestSample(ExtendTestCaseParams):

def setUp(self):

pass

def tearDown(self):

pass

def jiafa(self, input01, input02):

result = input01 + input02

return result

def test_jiafa(self):

input_01 = self.param[0]

input_02 = self.param[1]

expectedResult = self.param[2]

result = self.sub(input_01, input_02)

print(result)

self.assertEqual(result, expectedResult)

if __name__=='__main__':

testData = [

(10, 9, 19),

(12, 13, 25),

(12, 10, 22),

(2, 4, 6)

]

suite = unittest.TestSuite()

for i in testData:

suite.addTest(ExtendTestCaseParams.parametrize(ApiTestSample, 'test_jiafa', canshu=i))

runner = unittest.TextTestRunner()

runner.run(suite)

执行结果如下:

....

## 19

25

Ran 4 tests in 0.000s

22

6

OK

通过对 unittest 框架优化,我们实现了 unittest 框架的参数化,这样就可以用于接口测试了。虽然我们实现了参数化,但是测试结果的展示不够直观,这个时候需要一个可视化页面来直接显示测试结果。所幸的是,python 中有专门展示测试结果的框架:HTMLTestRunner。该框架可以将测试结果转换为 HTML 页面,并且该框架可以和unittest 框架完美的结合起来。接下来我们讲述一下 HTMLTestRunner 框架的使用。

3、测试结果可视化

HTMLTestRunner 框架可用来生成可视化测试报告,并能很好的与 unittest 框架结合使用,接下来我们以一段代码来展示一下 HTMLTestRunner 的使用。

if __name__=='__main__':

from HTMLTestRunner import HTMLTestRunner

testData = [

(10, 9, 19),

(12, 13, 25),

(12, 10, 22),

(2, 4, 6)

]

suite = unittest.TestSuite()

for i in testData:

suite.addTest(ExtendTestCaseParams.parametrize(ApiTestSample,'test_jiafa',canshu=i))

currentTime = time.strftime("%Y-%m-%d %H_%M_%S")

result_path = './test_results'

if not os.path.exists(path):

os.makedirs(path)

report_path = result_path + '/' + currentTime + "_report.html"

reportTitle = '测试报告'

desc = u'测试报告详情'

with open(report_path, 'wd') as f:

runner = HTMLTestRunner(stream=f, title=reportTitle, description=desc)

runner.run(suite)

测试结果如下:

下面详细讲解一下 html 报告的生成代码:

runner = HTMLTestRunner(stream=fp, title=reportTitle, description=desc)

HTMLTestRunner 中的 stream 表示输入流,这里我们将文件描述符传递给 stream,title 参数表示要输出的测试报告主题名称,description 参数是对测试报告的描述。在使用 HTMLTestRunner 时,有几点需要注意:

1)HTMLTestRunner 模块非 Python 自带库,需要到 HTMLTestRunner 的官网下载

该安装包;

2)官网的 HTMLTestRunner 模块仅支持 Python 2.x 版本,如果要在 Python 3.x中,需要修改部分代码,修改的代码部分请自行上网搜索;

如果需要生成 xml 格式,只需将上面代码中的

runner = HTMLTestRunner(stream=fp, title=reportTitle, description=desc)

runner.run(suite)

修改为如下代码

import xmlrunner

runner = xmlrunner.XMLTestRunner(output='report')

runner.run(suite)

4、接口测试分类

前面大家对接口请求,测试框架和测试结果可视化方面有了深入的了解。有了前面的基础,对于接下来理解和编写接口测试会有很大帮助。这里我们先来讲解一下接口测试与单元测试的区别。单元测试只针对函数进行多组参数测试,包括正常和异常参数组合。而接口测试是针对某一接口进行多组参数测试。实际接口测试中,我们又将接口测试分为两种:

1)单接口测试;

2)多接口测试。

对于单接口测试,只需针对单个接口测试,测试数据根据接口文档中的参数规则来设计测试用例;对多接口测试,首先要确保接口之间调用逻辑正确,然后再根据接口文档中的参数规则来设计用例进行测试。下面我就根据这两种不同情况的接口测试,用实际项目代码展示一下。

4.1 单接口测试

class TestApiSample(ExtendTestCaseParams):

def setUp(self):

pass

def tearDown(self):

pass

def register(self, ip, name, desc):

url = 'http://%s/api/v1/reg' % ip

headers = {"Content-Type": "application/x-www-form-urlencoded"}

para = {"app_name": name, "description": desc}

req = self.Post(url, para, headers)

return req

def test_register(self):

for index, value in enumerate(self.param):

print('Test Token {0} parameter is {1}'.format(index, value))

self.ip = self.param[1]

self.name = self.param[2]

self.desc = self.param[3]

self.expectedValue = self.param[4]

req = self.grant_register(self.ip, self.name, self.desc)

self.assertIn(req.status_code, self.expectedValue, msg="Test Failed.")

if __name__=='__main__':

import random

import string

ip = '172.36.17.108'

testData = [

(1, ip, ''.join(random.sample(string.ascii_letters + string.digits, 7)), '', 200),

(2, ip, ''.join(random.sample(string.ascii_letters + string.digits, 7)), '', 200),

(3, ip, ''.join(random.sample(string.ascii_letters + string.digits, 7)), '', 200)

]

suite = unittest.TestSuite()

for i in testData:

suite.addTest(ExtendTestCaseParams.parametrize(TestApiSample,'test_register',canshu=i))

currentTime = time.strftime("%Y-%m-%d %H_%M_%S")

path = './results'

if not os.path.exists(path):

os.makedirs(path)

report_path = path + '/' + currentTime + "_report.html"

reportTitle = '接口测试报告'

desc = u'接口测试报告详情'

with open(report_path, 'wd') as f:

runner = HTMLTestRunner(stream=f, title=reportTitle, description=desc)

runner.run(suite)

上述代码中的 register()为注册接口函数,test_register()为测试注册接口函数,testData 为测试数据,这里没有完全做到测试脚本与测试数据分离。为了实现测试数据与测试脚本分离,可以将 testData 列表单独写在文本文件或者数据库中,运行测试脚本时再去加载这些数据,就能实现测试脚本与测试数据的分离。

4.2 多接口测试

class TestApiSample(ExtendTestCaseParams):

def setUp(self):

pass

def tearDown(self):

pass

def register(self, ip, name, desc):

url = 'https://%s/api/v1/reg' % ip

headers = {"Content-Type": "application/x-www-form-urlencoded"}

para = {"app_name": name, "description": desc}

req = self.Post(url, para, headers)

return req

def oauth3_basic(self, ip, name, desc):

apps = self.register(ip, name, desc)

apps = apps.json()

url = 'http://%s/api/v1/basic' % ip

data = {"client_id":apps['appId'], "client_secret":apps['appKey']}

headers = None

req = requests.post(url, data, headers)

basic = str(req.content, encoding='utf-8')

return apps, basic, req

def test_oauth3_basic(self):

count = 0

for i in self.param:

count += 1

self.ip = self.param[1]

self.name = self.param[2]

self.desc = self.param[3]

self.expected = self.param[4]

apps, basic, req = self.oauth3_basic(self.ip, self.name, self.desc)

self.assertIn(req.status_code, self.expected, msg="Grant Failed.")

if __name__=='__main__':

import random

import string

ipAddr = '172.36.17.108'

testData = [

(1, ipAddr, ''.join(random.sample(string.ascii_letters + string.digits, 7)), '', 200),

(2, ipAddr, ''.join(random.sample(string.ascii_letters + string.digits, 7)), '', 200),

(3, ipAddr, ''.join(random.sample(string.ascii_letters + string.digits, 7)), '', 200)

]

suite = unittest.TestSuite()

for i in testData:

suite.addTest(ExtendTestCaseParams.parametrize(TestApiSample, 'test_oauth3_basic',

canshu=i))

currentTime = time.strftime("%Y-%m-%d %H_%M_%S")

path = '../Results'

if not os.path.exists(path):

os.makedirs(path)

report_path = path + '/' + currentTime + "_report.html"

reportTitle = '接口测试报告'

desc = u'接口测试报告详情'

with open(report_path, 'wd') as f:

runner = HTMLTestRunner(stream=f, title=reportTitle, description=desc)

runner.run(suite)

上述代码中,我们对两个接口进行了函数封装,两个接口之间有依赖关系,oauth3_basic()函数在请求之前必须先去请求 register()函数获取数据。对于这种多接口测试,且接口之间存在互相调用的情况,最好是在调用该接口前时,将互相之间有依赖的接口封装进该接口中,保证接口调用逻辑一致。其次再针对该接口的其它参数设计测试用例去测试该接口。

5、https 协议请求

前面我们提及的接口测试,仅是关于请求 http 协议的。然而,http 协议在传输过程中并不安全,通过该协议传输内容容易被截取,由此人们提出了 https 协议。该协议在原先的 http 协议之外,对传输过程中的内容进行了加密处理,这样就能确保信息在传输过程中的安全。目前很多公司的访问 url 都已转换到 https 协议。因此在接口测试中也要考虑到对 https 协议访问的支持。目前对于 https 协议访问的处理有以下几种方案。

第一种,对于一般网站访问,无法获得支持 https 协议的证书信息,因此只能选择忽略 ssl 校验;

第二种,对于外部网络访问公司内容网络和内容来说,除了要经过防火墙外,访问具体业务要经过负载均衡器。而负载均衡器一般要求支持 https 协议,这个时候就需要使用 Python 中的 ssl 模块对证书进行校验;

关于忽略访问 https 协议的证书校验,这里忽略不表。重点讲解 https 协议证书的校验。在 Python 中,提供了 ssl 模块,用于对 https 协议证书的认证。这里以一段代码来展示该模块的应用。

import ssl

cont = ssl.SSLContext(ssl.PROTOCOL_SSLv23)

cont.check_hostname = False

cont.load_cert_chain(certfile=public_key, keyfile=private_key)

cont.verify_mode = 2

cont.load_verify_locations(ca_key)

上述代码中先生成 ssl 上下文对象 cont,接下来用这个上下文对象 cont 依次进行域名校验、证书导入、验证模式选择及 CA 证书验证。cont.checkhostname 用于域名校验,值为 True 表示进行主机名校验,值为 False 表示不进行主机名校验。

cont.loadcertchain(certfile=publickey, keyfile=privatekey),certfile 表示导入公钥证书,keyfile 表示导入私钥证书。一般情况下,Python 支持的 certfile 证书文件后缀为.crt,keyfile 证书文件后缀为.pem。cont.verifymode 为验证模式,值为 0 表示不做证书校验,值为 1 表示代表可选,值为 2 表示做证书校验。cont.loadverifylocations(ca_key)表示导入CA 证书。一般的证书校验都要经过上述这几个步骤。此时 ssl 证书的基本配置已完成。接下来就需要在发送 https 请求时加入证书验证环节,示例代码如下:

req = request.Request(url=url, data=para, headers=headers, method='GET')

response = request.urlopen(req, context=self.context)

整个完整的 ssl 证书验证代码如下:

if __name__=='__main__':

from urllib import parse, request

import ssl

context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)

context.check_hostname = False

context.load_cert_chain(certfile=pub_key_cert_file, keyfile=pri_key_pem_file)

context.verify_mode = 2

context.load_verify_locations(ca_file)

req = request.Request(url=url, data=para, headers=headers, method='GET')

response = request.urlopen(req, context=self.context)

上述代码中,我们选择了 python 中 urllib 模块做接口请求,是因为在多次对比了reuests模块和 urllib 对 https 证书验证的支持之后,发现 urllib 模块能够很好地支持 ssl 证书校验。更多有关 python 中 ssl 模块的信息,请参考 ssl 官方文档

6、总结

回顾整个项目经过,应该说是是被现实问题逼着进步,从一开始的走捷径使用 API集成工具来测试接口,到后来使用自动化测试脚本实现接口测试,再到最后增加对 https协议的支持。这一路走来,带着遇到问题解决问题地思路,我的测试技能得到很大提升。总结这几个月的项目经历就一句话:遇到问题,解决问题,能力才会得到快速提升,与大家共勉。

请关注官方公众号"Atstudy网校",回复关键词:"学习"就可以拿到软件测试学习资料~~

测试 接口 参数 模块 代码 框架 证书 函数 支持 数据 结果 报告 脚本 验证 方法 接下来 两个 单元 项目 封装 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 5e被禁用服务器怎么解除 米花网络技术 6700k可以做服务器cpu吗 方舟大型服务器一个月能挣多少钱 聊天服务器怎么登录 软件开发待遇好不好 中邦网络安全技术有限公司 税控服务器管理系统是干什么的 正规网络技术销售公司 软件开发后还能干什么 access数据库怎么弄照片 中小学如何开展网络安全教育 网络安全在国防中的重要意义 北京高科技软件开发诚信合作 跨平台的桌面软件开发 如何用国外社交软件开发客户 oracle数据库难学吗 领导对网络安全的要求 软件开发过程中最艰巨的脑力 互联网科技行业交税 服务器磁盘跑满怎么处理 海康cvr服务器时间怎么调 数据库复杂表如何创建 it服务器搭建私有云 hpe塔式服务器多少钱 经信局对企业网络安全的考核 什么是网络安全有何特性 扬州工程管理软件开发平台 oracle数据库免费 中国网络安全年会瑞莱智慧
0