1.项目介绍
整个项目分为四个部分:接口基础丶接口开发丶Unittest与接口测试结合以及接口自动化框架从设计到开发
接口基础包括:HTTP接口 / 常见接口 / 接口工具 / 接口基础知识
接口开发:通过Django来开发get/post接口
Unittest与接口测试结合:unittest应用 / 断言 / requests引入 / HTMLTestRunner / case的管理
接口自动化框架从设计到开发:如何设计框架 / 封装工具类 / 重构基类 / 错误调试 / 结果收集以及处理 / 解决数据依赖 / 结果统计及报告发送
项目整体思路:通过对接口数据文档的读写操作,来获取文档中case的所有数据,然后通过requests模块来发送请求获取的响应数据,通过返回的响应数据中的某个标志性字段的值来判断是否测试成功或者失败,最后将测试的结果数据写入到测试文档或者是html页面又或者是将结果以邮件的形式发送到指定邮箱,这是整个大框架思路,要完成这一系列自动化的测试框架,则需要有一定的python代码基础,博主这里只是粗略的叙述了思路,有很多地方就不细说了比如数据依赖等就请大家慢慢的阅读吧
2.测试报告效果预览
1.什么是接口
连接前后端以及移动端,通俗来说就是前端和后端之间的桥梁,比如网站需要去调用银行丶微信及支付宝的接口来完成业务需求
2.接口的种类
外部接口和内部接口;内部接口又分为上层服务与下层服务以及同级服务
3.接口的分类
请求方式:post丶get丶delete丶put
4.为什么要做接口测试
原因:不同端的工作进度肯定是不一致的,那么就需要对最开始开发出来的接口进行测试;对于项目来说缩短项目周期,提高开发效率以及提高系统的健壮性
5.接口测试流程
需求讨论——需求评审——场景设计——用例设计——数据准备——执行
6.为什么要设计测试用例
7.用例设计分类
功能用例测试:测试功能是否正常丶测试功能是否按照接口文档实现
逻辑用例设计:是否存在依赖业务,例如有些操作是需要用户登录成功的状态下才能进行的操作
异常测试用例设计:参数异常以及数据异常;参数异常包括关键字参数丶参数为空丶多参数丶少参数丶错误参数,数据异常包括关键字数据丶数据为空丶长度不一致丶错误数据
安全测试用例设计:cookie丶header丶唯一识别码
1.接口测试工具分类
httpwatch:集成于IE和Firefox浏览器中,在其他浏览器中无法使用,查看数据也比较麻烦
wireshark:只要是经过电脑的所有请求都会去抓取,导致数据量比较庞大,看数据也比较麻烦
fiddler:轻量级抓包工具,功能比较全,只会记录http请求不会像wireshark工具记录tcp和udp等请求
loadrunner:不仅仅是性能测试工具,由于该工具几乎都是基于http请求,所以也可以用来测试接口
fiddler:它除了可以抓包还可以向接口发送各种请求
soapui:接口和自动化测试工具,功能也比较强大
jmeter:跟loadrunner一样不仅仅是做性能测试,也可以对接口进行测试
1.抓取不同类型接口数据(http以及https)
2.数据模拟以及过滤规则
3.如何模拟接口响应数据
4.使用fiddler进行评论接口测试
请求头数据
请求体数据
1.unittest简单使用
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/17 13:10'
import unittest
class TestMethod(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("Method before class execution")
@classmethod
def tearDownClass(cls):
print("Method after class execution")
def setUp(self):
print("------setUp------")
def tearDown(self):
print("------tearDown------")
def test_01(self):
print("First test method")
def test_02(self):
print("The second test method")
if __name__ == '__main__':
unittest.main()
2.unittest和request重构封装
说明:使用requests模块对接口url地址发送请求,通过unittest测试框架进行case测试
import requests
class RunMain:
def send_get(self,url,data):
res = requests.get(url=url,data=data).json()
return res
def send_post(self,url,data):
res = requests.post(url=url,data=data).json()
return res
def run_main(self,url,method,data=None):
res = None
if method == 'GET':
res = self.send_get(url,data)
else:
res = self.send_post(url,data)
return res
import unittest
import json
import HtmlTestRunner
from .demo import RunMain
class TestMethod(unittest.TestCase):
def setUp(self):
self.run = RunMain()
def test_01(self):
url = 'http://api.ishugui.com/asg/portal/call/265.do'
data = {
"sstoken":"eyJleHAiOjE1Njg1MzgyNTczMzUsImlhdCI6MTU2MDc2MjI1NzMzNSwicHAiOiIxMTQwNTQ1Njg5MDYwMDQ0ODAwQHNvaHUuY29tIiwidGsiOiIwZkNYSHpjTUZzR0dFMEswbVdvUVFCNWVCanpXa0hmWiIsInYiOjB9.SDYkT9FpWrBbko6xRrESN74IXJhzkqQLtijKjGiVrqA",
"gidinf":"x011060802ff0fd40695d68140002799751474c540b3",
"ppinf":"2|1560762257|1561971857|bG9naW5pZDowOnx1c2VyaWQ6Mjg6MTE0MDU0NTY4OTA2MDA0NDgwMEBzb2h1LmNvbXxzZXJ2aWNldXNlOjMwOjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHxjcnQ6MTA6MjAxOS0wNi0xN3xlbXQ6MTowfGFwcGlkOjY6MTEwNjA4fHRydXN0OjE6MXxwYXJ0bmVyaWQ6MTowfHJlbGF0aW9uOjA6fHV1aWQ6MTY6czk1YWIwNDk5NjE3YmJhNnx1aWQ6MTY6czk1YWIwNDk5NjE3YmJhNnx1bmlxbmFtZTowOnw",
"pprdig":"kaKPdU0WwIdzL58CqxNz5pgMyv23P0-Y5GRnd5ufPlXIGzrk7_7TlIK5XFQiuoqAHNqGVXHCVd4cB1DIkR5yFZ_nExnSjIZbBJWYlMkrsiIjDYqWCvedZRLm8sZqS0WqA0FcKXuSn3Z0gVRus9YpEonNz5wyuWdUqxaSmzlzygY",
"ppsmu":"1|1560762257|1561971857|dXNlcmlkOjI4OjExNDA1NDU2ODkwNjAwNDQ4MDBAc29odS5jb218dWlkOjA6fHV1aWQ6MDo|byWcaoPqy02s2_9GHLhZFAQ6Ov_GazMPFLrq115HiSTBS9Ijr33a55quRq2Mr1_6ZMruKEk-BYFpShUaMtwRYA"
}
res1 = self.run.run_main(url, "POST", json.dumps(data))
print(res1)
def test_02(self):
url = 'http://api.ishugui.com/asg/portal/call/265.do'
data = {
}
res2 = self.run.run_main(url, 'POST', data)
print(res2)
if __name__ == '__main__':
unittest.main()
3.unittest中assert的使用
class TestMethod(unittest.TestCase):
def setUp(self):
self.run = RunMain()
def test_01(self):
url = 'http://api.ishugui.com/asg/portal/call/265.do'
data = {
"sstoken":"eyJleHAiOjE1Njg1MzgyNTczMzUsImlhdCI6MTU2MDc2MjI1NzMzNSwicHAiOiIxMTQwNTQ1Njg5MDYwMDQ0ODAwQHNvaHUuY29tIiwidGsiOiIwZkNYSHpjTUZzR0dFMEswbVdvUVFCNWVCanpXa0hmWiIsInYiOjB9.SDYkT9FpWrBbko6xRrESN74IXJhzkqQLtijKjGiVrqA",
"gidinf":"x011060802ff0fd40695d68140002799751474c540b3",
"ppinf":"2|1560762257|1561971857|bG9naW5pZDowOnx1c2VyaWQ6Mjg6MTE0MDU0NTY4OTA2MDA0NDgwMEBzb2h1LmNvbXxzZXJ2aWNldXNlOjMwOjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHxjcnQ6MTA6MjAxOS0wNi0xN3xlbXQ6MTowfGFwcGlkOjY6MTEwNjA4fHRydXN0OjE6MXxwYXJ0bmVyaWQ6MTowfHJlbGF0aW9uOjA6fHV1aWQ6MTY6czk1YWIwNDk5NjE3YmJhNnx1aWQ6MTY6czk1YWIwNDk5NjE3YmJhNnx1bmlxbmFtZTowOnw",
"pprdig":"kaKPdU0WwIdzL58CqxNz5pgMyv23P0-Y5GRnd5ufPlXIGzrk7_7TlIK5XFQiuoqAHNqGVXHCVd4cB1DIkR5yFZ_nExnSjIZbBJWYlMkrsiIjDYqWCvedZRLm8sZqS0WqA0FcKXuSn3Z0gVRus9YpEonNz5wyuWdUqxaSmzlzygY",
"ppsmu":"1|1560762257|1561971857|dXNlcmlkOjI4OjExNDA1NDU2ODkwNjAwNDQ4MDBAc29odS5jb218dWlkOjA6fHV1aWQ6MDo|byWcaoPqy02s2_9GHLhZFAQ6Ov_GazMPFLrq115HiSTBS9Ijr33a55quRq2Mr1_6ZMruKEk-BYFpShUaMtwRYA"
}
res1 = self.run.run_main(url, "POST", json.dumps(data))
# print(type(res1))
# print(res1['pub'])
# print(type(res1['pub']))
if res1['pub']['status'] == 0:
print("测试通过")
else:
print("测试失败")
print(res1)
def test_02(self):
url = 'http://api.ishugui.com/asg/portal/call/265.do'
data = {
}
res2 = self.run.run_main(url, 'POST', data)
if res2['pub']['status'] == 0:
print("测试通过")
else:
print("测试失败")
print(res2)
if __name__ == '__main__':
unittest.main()
class TestMethod(unittest.TestCase):
def setUp(self):
self.run = RunMain()
def test_01(self):
url = 'http://api.ishugui.com/asg/portal/call/265.do'
data = {
"sstoken":"eyJleHAiOjE1Njg1MzgyNTczMzUsImlhdCI6MTU2MDc2MjI1NzMzNSwicHAiOiIxMTQwNTQ1Njg5MDYwMDQ0ODAwQHNvaHUuY29tIiwidGsiOiIwZkNYSHpjTUZzR0dFMEswbVdvUVFCNWVCanpXa0hmWiIsInYiOjB9.SDYkT9FpWrBbko6xRrESN74IXJhzkqQLtijKjGiVrqA",
"gidinf":"x011060802ff0fd40695d68140002799751474c540b3",
"ppinf":"2|1560762257|1561971857|bG9naW5pZDowOnx1c2VyaWQ6Mjg6MTE0MDU0NTY4OTA2MDA0NDgwMEBzb2h1LmNvbXxzZXJ2aWNldXNlOjMwOjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHxjcnQ6MTA6MjAxOS0wNi0xN3xlbXQ6MTowfGFwcGlkOjY6MTEwNjA4fHRydXN0OjE6MXxwYXJ0bmVyaWQ6MTowfHJlbGF0aW9uOjA6fHV1aWQ6MTY6czk1YWIwNDk5NjE3YmJhNnx1aWQ6MTY6czk1YWIwNDk5NjE3YmJhNnx1bmlxbmFtZTowOnw",
"pprdig":"kaKPdU0WwIdzL58CqxNz5pgMyv23P0-Y5GRnd5ufPlXIGzrk7_7TlIK5XFQiuoqAHNqGVXHCVd4cB1DIkR5yFZ_nExnSjIZbBJWYlMkrsiIjDYqWCvedZRLm8sZqS0WqA0FcKXuSn3Z0gVRus9YpEonNz5wyuWdUqxaSmzlzygY",
"ppsmu":"1|1560762257|1561971857|dXNlcmlkOjI4OjExNDA1NDU2ODkwNjAwNDQ4MDBAc29odS5jb218dWlkOjA6fHV1aWQ6MDo|byWcaoPqy02s2_9GHLhZFAQ6Ov_GazMPFLrq115HiSTBS9Ijr33a55quRq2Mr1_6ZMruKEk-BYFpShUaMtwRYA"
}
res1 = self.run.run_main(url, "POST", json.dumps(data))
# print(type(res1))
# print(res1['pub'])
# print(type(res1['pub']))
# if res1['pub']['status'] == 0:
# print("测试通过")
# else:
# print("测试失败")
self.assertEqual(res1['pub']['status'], 0, "测试失败")
print(res1)
def test_02(self):
url = 'http://api.ishugui.com/asg/portal/call/265.do'
data = {
}
res2 = self.run.run_main(url, 'POST', data)
# if res2['pub']['status'] == 0:
# print("测试通过")
# else:
# print("测试失败")
self.assertEqual(res2['pub']['status'], 0, "测试失败")
print(res2)
if __name__ == '__main__':
unittest.main()
4.unittest中case的管理及运用
5.unittest和HTMLTestRunner结合生成报告(博主这里给大家展现两种)
第一种:比较新版本的htmltestrunner报告
第二种:比较经典版本的htmltestrunner报告
第94行, 将import StringIO修改成import io
第539行,将self.outputBuffer = StringIO.StringIO()修改成self.outputBuffer = io.StringIO()
第642行,将if not rmap.has_key(cls):修改成if not cls in rmap:
第631行,将print >> sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime)修改成print(sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime))
第766行,将uo = o.decode('latin-1')修改成uo = e
第775行,将ue = e.decode('latin-1')修改成ue = e
1.mock简介
mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法,mock是在测试过程中,对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为即就是模拟fiddler返回接口响应数据的一个过程。
2.mock安装
3.在case中通过底层函数实现mock
mock_data = mock.Mock(return_value=data)
print(mock_data)
4.重构封装mock服务
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/20 16:26'
from mock import mock
import json
def mock_test(mock_method, request_data, url, method, response_data):
"""
:param mock_method:
:param request_data:
:param url:
:param method:
:param response_data:
:return: res
"""
mock_method = mock.Mock(return_value=response_data)
print('mock_method:', mock_method)
res = mock_method(url, method, json.dumps(request_data))
return res
res1 = mock_test(self.run.run_main, data, url, 'POST', 'ssssssss')
print('res1:', res1)
1.如何设计一个接口自动化测试框架
根据接口地址丶接口类型丶请求数据丶预期结果来进行设计,对于需要登录后才能进行操作的接口那么则需要进行header cookie等数据的传递,自动化测试的难点就是数据依赖。
2.python操作excel获得内容
3.重构操作excel函数
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/20 17:33'
import xlrd
data = xlrd.open_workbook("../test_data/rs.xls")
tables = data.sheets()[0] # 获取表格数据对象
print(tables.nrows) # 打印表格行数
print(tables.cell_value(0,0)) # 打印excel表格数据,需要传递数据所在的坐标(x,y)
print(tables.cell_value(0,1))
print("*"*50+"封装前后数据对比"+"*"*50)
class operationExcel(object):
def __init__(self, file_path="../test_data/rs.xls", sheet_id=0):
self.file_path = file_path
self.sheet_id = sheet_id
self.data = self.get_data()
def get_data(self):
data = xlrd.open_workbook(self.file_path)
tables = data.sheets()[self.sheet_id]
return tables
def get_rows(self):
"""获取单元格的排数"""
return self.data.nrows
def get_cell_value(self, x=0, y=0):
"""获取某个单元格的数据"""
return self.data.cell_value(x, y)
if __name__ == '__main__':
print(operationExcel().get_rows())
print(operationExcel().get_cell_value())
print(operationExcel().get_cell_value(0,1))
4.学习操作json文件
5.重构json工具类
class operationJson(object):
def __init__(self, file_path="../test_data/login.json"):
self.file_path = file_path
self.data = self.get_data()
def get_data(self):
with open(self.file_path) as f:
data = json.load(f)
return data
def get_key_words(self, key=None):
if key:
return self.data[key]
else:
return self.data
if __name__ == '__main__':
print(operationJson().get_key_words())
print(operationJson().get_key_words("login"))
print(operationJson().get_key_words("login")['username'])
6.封装获取常量方法
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/21 9:29'
class global_var:
id = '0' # id
module = '1' # 模块
url = '2' # url
run = '3' # 是否运行
request_type = '4' # 请求类型
request_header = '5' # 是否携带header
case_depend = '6' # case依赖
response_data_depend = '7' # 依赖的返回数据
data_depend = '8' # 数据依赖
request_data = '9' # 请求数据
expect_result = '10' # 预期结果
reality_result = '11' # 实际结果
def get_id():
return global_var.id
def get_module():
return global_var.module
def get_url():
return global_var.url
def get_run():
return global_var.run
def get_request_type():
return global_var.request_type
def get_request_header():
return global_var.request_header
def get_case_depend():
return global_var.case_depend
def get_response_data_depend():
return global_var.response_data_depend
def get_data_depend():
return global_var.data_depend
def get_request_data():
return global_var.request_data
def get_expect_result():
return global_var.expect_result
def get_reality_result():
return global_var.reality_result
7.封装获取接口数据
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/21 10:01'
from utils.op_excel import operationExcel
from utils.op_json import operationJson
from data import data_conf
class getData(object):
def __init__(self):
self.op_excel = operationExcel()
def get_case_lines(self):
"""获取表格行数"""
return self.op_excel.get_rows()
def get_is_run(self, x):
"""获取case是否运行"""
flag = None
y = data_conf.get_run()
run_value = self.op_excel.get_cell_value(x, y)
if run_value == 'yes':
flag = True
else:
flag = False
return flag
def get_is_header(self, x):
"""是否携带header"""
y = data_conf.get_request_header()
header = self.op_excel.get_cell_value(x, y)
if header == 'yes':
return data_conf.get_header_value()
else:
return None
def get_request_method(self, x):
"""获取请求方式"""
y = data_conf.get_request_type()
request_method = self.op_excel.get_cell_value(x, y)
return request_method
def get_request_url(self, x):
"""获取请求地址"""
y = data_conf.get_url()
request_url = self.op_excel.get_cell_value(x, y)
return request_url
def get_request_data(self, x):
"""获取请求数据"""
y = data_conf.get_request_data()
request_data = self.op_excel.get_cell_value(x, y)
if request_data == '':
return None
return request_data
def get_data_for_json(self, x):
"""通过excel中的关键字去获取json数据"""
op_json = operationJson()
data = op_json.get_key_words(self.get_request_data(x))
return data
def get_expect_data(self, x):
"""获取预期结果数据"""
y = data_conf.get_expect_result()
expect_data = self.op_excel.get_cell_value(x, y)
if expect_data == '':
return None
return expect_data
8.post、get基类的封装
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/21 11:19'
import requests
class RunMain(object):
def get_main(self, url, data=None, header=None):
res = None
if header is not None:
res = requests.get(url=url, data=data, headers=header).json()
else:
res = requests.get(url=url, data=data).json()
return res
def post_main(self, url, data, header=None):
res = None
if header is not None:
res = requests.post(url=url, data=data, headers=header).json()
else:
res = requests.post(url=url, data=data).json()
return res
def run_main(self, url, method, data=None, header=None):
res = None
if method.lower() == 'post':
res = self.post_main(url, data, header)
elif method.lower() == 'get':
res = self.get_main(url, data, header)
else:
return "what ?????"
return res
9.主流程封装及错误解决调试
# -*- coding: utf-8 -*-
__author__ = 'cdtaogang'
__date__ = '2019/6/21 11:57'
from base.run_method import RunMain
from data.data_get import getData
class RunTest(object):
def __init__(self):
self.runmain = RunMain()
self.data = getData()
def run(self):
res = None
row_counts = self.data.get_case_lines() # 获取excel表格行数
# print(row_counts) 5
for row_count in range(1, row_counts):
# print(row_count) 1,2,3,4
url = self.data.get_request_url(row_count) # y行不变遍历获取x列的请求地址
method = self.data.get_request_method(row_count) # y行不变遍历获取x列的请求方式
is_run = self.data.get_is_run(row_count) # y行不变遍历获取x列的是否运行
data = self.data.get_data_for_json(row_count) # y行不变遍历获取x列的请求数据,这里面时三次调用,依次分别是get_data_for_json丶get_key_words丶get_request_data
header = self.data.get_is_header(row_count)
print('url:', url)
print('method:', method)
print('is_run:', is_run)
print('data:', data)
print('header:', header)
if is_run:
res = self.runmain.run_main(url,method,data,header)
print("*"*60+"分割线"+"*"*60)
return res
if __name__ == '__main__':
print('res:', RunTest().run())
运行run_test,成功的将excel以及json数据正确打印出来,返回res服务器返回结果,需要说明的是excel表中的所有数据都不是真实存在的,包括json文档数据也是,这里主要是测试整个框架的正确性读取excel以及json文档数据,并正确的发送请求获得相应数据
10. 返回数据格式处理以及调错
??自动化测试相关教程推荐:
2023最新自动化测试自学教程新手小白26天入门最详细教程,目前已有300多人通过学习这套教程入职大厂!!_哔哩哔哩_bilibili
2023最新合集Python自动化测试开发框架【全栈/实战/教程】合集精华,学完年薪40W+_哔哩哔哩_bilibili
测试开发相关教程推荐
2023全网最牛,字节测试开发大佬现场教学,从零开始教你成为年薪百万的测试开发工程师_哔哩哔哩_bilibili
postman/jmeter/fiddler测试工具类教程推荐
讲的最详细JMeter接口测试/接口自动化测试项目实战合集教程,学jmeter接口测试一套教程就够了!!_哔哩哔哩_bilibili
2023自学fiddler抓包,请一定要看完【如何1天学会fiddler抓包】的全网最详细视频教程!!_哔哩哔哩_bilibili
2023全网封神,B站讲的最详细的Postman接口测试实战教学,小白都能学会_哔哩哔哩_bilibili
??
??
如果对你有帮助的话,点个赞收个藏,给作者一个鼓励。也方便你下次能够快速查找。
如有不懂还要咨询下方小卡片,博主也希望和志同道合的测试人员一起学习进步
在适当的年龄,选择适当的岗位,尽量去发挥好自己的优势。
我的自动化测试开发之路,一路走来都离不每个阶段的计划,因为自己喜欢规划和总结,
测试开发视频教程、学习笔记领取传送门!!
?