最简单最基本的接口自动化测试框架,python+request+unittest
文件结构
config.ini中存放HTTP、email、数据库等信息,可以在email中加入on_off=on; =on 则不发邮件。
[DATABASE]
# ip地址
host = 50.23.190.57
# 用户名
username = xxxxxx
# 密码
password = ******
# 端口
port = 3306
# 数据库名称
database = flychord
[HTTP]
# 协议
scheme=http
#地址
baseurl = 192.168.X.XX
# 端口
port =8080
# 超时时间
timeout = 10.0
headers = {'POST http': '//XXXX.XXXX.com/member/memberchpoexercise/addgamesexercise/ HTTP/1.1','Expect': '100-continue', 'X-Unity-Version': '2018.3.1f1','Content-Type': 'application/x-www-form-urlencoded', 'token': '467cb87f1e5f7a3d10ed18d8b41842c5','version': '1.0','timestamp': '1564046939039','encrypt': '0','ports': 'AU', 'os': 'android','Content-Length': '103','User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 7.0; KOB-W09 Build/HUAWEIKOB-W09)','Host': 'flychordtest.pianosedu.com', 'Connection': 'Keep-Alive','Accept-Encoding': 'gzip'}
[EMAIL]
mail_host = smtp.163.com
mail_user = xxx@163.com
mail_pass = *********
mail_port = 25
sender = xxx@163.com
receiver = xxxx@qq.com/xxxx@qq.com
subject = python
content = "All interface test has been complited\nplease read the report file about the detile of result in the attachment."
testuser = Someone
on_off = 1
用于获取某路径下文件的绝对路径
import os
def get_Path():
path = os.path.split(os.path.realpath(__file__))[0]
return path
用于读取config.ini中的配置信息
import os
from FlychordInterfaceTest import getPathInfo
import configparser # 引入我们自己的写的获取路径的类
path = getPathInfo.get_Path() # 调用实例化,path:D:\Testing\jiaoben\FlychordInterfaceTest
config_path = os.path.join(path,'config.ini') # 在path路径下再加一级,configPath:D:\Testing\jiaoben\FlychordInterfaceTest\config.ini
config = configparser.ConfigParser() # 调用外部的读取配置文件的方法
config.read(config_path, encoding='utf-8') # 打开配置文件
def get_email(name):
value = config.get("EMAIL", name)
return value
def get_http(name):
value = config.get("HTTP", name)
return value
def get_mysql(name): # 写好,留以后备用。但是因为我们没有对数据库的操作,所以这个可以屏蔽掉
value = config.get("DATABASE", name)
return value
# print(type(get_http('headers')))
用于读取用例文档或接口文档
import xlrd
import os
import json
from FlychordInterfaceTest import getPathInfo
class Excel():
# 初始化方法 参数type:为r是读取excel,参数file_name是将要读取的文件,参数index是将要读取的那一页
def __init__(self, file_name, index):
path = getPathInfo.get_Path()
excel_path = os.path.join(path, 'file', file_name)
# 打开文件
self.data = xlrd.open_workbook(excel_path)
# 选取需要读取数据的那一页
self.sheet1 = self.data.sheet_by_index(index)
# 获得行数和列数
self.rows1 = self.sheet1.nrows
self.cols1 = self.sheet1.ncols
# 装载所有数据的list
self.listvalue = []
# 读取数据
def read(self):
# 从第二行开始读取
for i in range(1, self.rows1):
value = []
for j in range(0, self.cols1):
# 读取表格内容按行,列取值
'''ctype: 0 empty, 1 string, 2 number, 3 date, 4 boolean, 5 error'''
ctype = self.sheet1.cell(i, j).ctype # 获取数据的类型
cell = self.sheet1.cell(i, j).value # 获取数据的值
# 判断Excel数据是否为整型
if ctype == 2 and cell % 1 == 0: # 如果是整形
cell = int(cell)
# 判断Excel数据是否为时间格式
elif ctype == 3:
# 转成datetime对象
from datetime import datetime
from xlrd import xldate_as_tuple
date = datetime(*xldate_as_tuple(cell, 0))
cell = date.strftime('%Y-%d-%m %H:%M:%S')
# 判断Excel数据是否为布尔值
elif ctype == 4:
cell = True if cell == 1 else False
# 存储到数组
value.append(cell)
self.listvalue.append(value)
return self.listvalue
# 将元素和链接表处理为json格式方便进行查询
def element_tojson(element):
elements = {}
# 讲元素和接口等信息组成key和value的形式方便进行查询
for e in element:
elements[e[0]] = {'type': e[1], 'url': e[2]}
return elements
# 将元素和链接表处理为list格式方便进行查询
def element_list(element):
elements = []
for e in element:
a = []
a.append(e[1])
a.append(e[4])
a.append(e[6])
a.append(to_json(e[9]))
a.append(to_json(e[10]))
elements.append(a)
return elements
def to_json(b):
# 过滤换行和空格
a = b.replace('\n', '').replace(' ', '')
# 判断是否为josn数据
if a[0] == '{':
params = json.loads(a)
# print(params)
return params
else:
# print(a)
return a
if __name__ == '__main__':
excel = Excel('DATA.xlsx', 0).read()
for i in range(0, 3):
to_json(excel[i][10])
# print(excel.read()[0][1]) # 用例标题
# print(excel.read()[0][4]) # 请求方法
# print(excel.read()[0][6]) # 接口路径
# print(excel.read()[0][9]) # 接口请求参数
# print(excel.read()[0][10]) # 接口返回参数
先创建common文件夹,confighttp.py用于等着接口请求
import requests
def send_post(url, data, headers):
result = requests.post(url=url, headers=headers, data=data).json()
return result
def send_get(url, data, headers):
result = requests.get(url=url, headers=headers, params=data).json()
return result
def run_main(method, url=None, data=None, headers=None): # 定义一个run_main函数,通过传过来的method来进行不同的get或post请求
result = None
method = method.lower()
if method == 'post':
result = send_post(url, data, headers)
elif method == 'get':
result = send_get(url, data, headers)
else:
print("method值错误!!!")
return result
if __name__ == '__main__':
result1 = run_main('get', 'http://192.168.1.155:8080//public/timestamp')
print(result1)
获取URL组成请求地址
from FlychordInterfaceTest import readConfig
def get_url(interface):
if readConfig.get_http('port') is None:
url = readConfig.get_http('scheme') + '://' + readConfig.get_http('baseurl') + interface
else:
url = readConfig.get_http('scheme') + '://' + readConfig.get_http('baseurl') + ":" + readConfig.get_http(
'port') + interface
return url
简易版:
import unittest # 导包
from FlychordInterfaceTest import readExcel, getURL
from FlychordInterfaceTest.common import configHttp
# 获取接口文档第一页数据
excel = readExcel.Excel('DATA.xlsx', 0).read()
class test01(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_01(self):
"""查看琴行详情"""
self.checkResult(excel[0][4], excel[0][6], excel[0][9])
def checkResult(self, method, interface, data, headers=None):
url = getURL.get_url(interface)
print(url)
info = configHttp.run_main(method, url, data, headers)
print(info)
self.assertEqual(int(info['code']), 404)
if __name__ == '__main__':
unittest.main(verbosity=1)
等级+1版:
import unittest # 导包
import ddt
from FlychordInterfaceTest import readExcel, getURL
from FlychordInterfaceTest.common import configHttp
# 获取接口文档第一页数据
excel = readExcel.element_list(readExcel.Excel('DATA.xlsx', 0).read())
@ddt.ddt
class test02(unittest.TestCase):
@classmethod
def setUpClass(cls): # 所有用例运行之前执行一次
pass
@classmethod
def tearDownClass(cls): # 所有用例运行之后执行一次
pass
@ddt.data(*excel)
@ddt.unpack
def test_02(self, title, method, path, request, response):
self._testMethodDoc = title # 动态的用例描述
self._testMethodName = title
info = self.checkResult(method, path, request)
self.assertEqual(response['success'], info['success'])
def checkResult(self, method, interface, data, headers=None):
url = getURL.get_url(interface)
try:
info = configHttp.run_main(method, url, data, headers)
return info
except:
return '接口请求出错'
if __name__ == '__main__':
unittest.main(verbosity=1)
ddt.ddt:装饰类,也就是继承TestCase的类。
ddt.data:装饰测试方法,参数是一系列的值。
ddt.file_data:装饰测试方法,参数是文件名。文件可以是json或者yaml类型。
注意,如果文件是以“.yml”或者".yaml"结尾,ddt会作为yaml类型处理,其他文件都会作为json文件处理。
如果文件是列表,列表的值会作为测试用例参数,同时,会作为测试用例方法名后缀显示。
如果文件是字典,字典的key会作为测试用例方法的后缀显示,字典的value会作为测试用例参数。
ddt.unpack:传递的是复杂的数据结构时使用,比如使用列表或者元组,添加unpack后,ddt会自动把元组或者列表对应到多个参数上。
@data(a,b)
那么a和b各运行一次用例
@data([a,d],[c,d])
如果没有unpack,那么[a,b]当成一个参数传入用例运行
如果有unpack,那么[a,b]被分解开,按照用例中的两个参数传递
脚本执行入口
import unittest
from time import strftime
# from logs.logs import DelAllLog
# from smtp.SendMail import sendMail
from BeautifulReport import BeautifulReport
# 删除所有旧日志
# DelAllLog()
# 格式化获取时间 以时间戳命名时不能用冒号(:)
ctime = strftime('%Y-%m-%d %H-%M-%S')
report_name = ctime + '-report.html'
test_suite = unittest.defaultTestLoader.discover('./case', pattern='test*.py')
# 调用测试报告框架
runner = BeautifulReport(test_suite)
# 定义测试报告相关的参数
runner.report(filename=report_name, description='琴行端自动化测试报告', log_path='./report')
# 发送邮件
# sendMail()
# 程序的执行入口
if __name__ == "__main__":
unittest.main()