函数编程12--函数原型-使用函数实现测试框架
什么是自动化测试框架
框架是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法
也可以认为,框架是可被应用开发者定制的应用骨架
自动化测试框架由一个或多个自动化测试基础模块、自动化测试管理模块、自动化测试统计模块等组成的工具集合
使用函数实现测试框架配置
在project_h1中修改
1)conf目录--存放配置文件
功能:存放入口地址、接口服务器、数据库服务器的配置信息
文件内容:
entry.ini
server.conf
db.conf
2)initsqls目录 -- 存放初始化sql语句文件
功能:存放初始化sql文件,按业务或人员分工区分
文件内容:login.sql
signup.sql
3)excelcase目录 --存放excel用例
功能:存放测试用例数据,按业务或人员分工区分
例如:用例一般统一文件类型和格式,如case_name、data、expect、expectsql、dbexpect
case_name:用例名称,也可以用用例id
data:请求数据,通常为字典格式{ }
expect:预期结果,可以是字符串或字典格式
expectsql:查询落库预期结果的sql语句
dbexpect:落库预期结果
文件内容:login.xlsx
signup.xlsx
4)log目录 --存放日志
功能:存放测试日志,通过代码自动写日志
5)common目录 -- 存放公用函数
功能:存放公共函数,包括读server配置文件、初始化数据库、读案例数据、执行测试、生成日志等相关函数
6)整体函数 --放入common目录
log() -- 日志函数
entry() -- 读入口信息
server_info() -- 获得服务器信息
db_info --数据库信息
read_sqls(sqlfiles=[]) --读取数据库sql语句
conn_db() -- 连接数据库
init_db(sqlfiles=[]) -- 初始化数据库
read_cases(casefile,columns=None) -- 读取测试用例
check_db(case_name,expectsql,dbexpect) -- 落库检查
post(url,case_name,data,expect) -- 发送数据
test_login() -- 测试函数
test_signup() -- 测试函数

路径的写法
test.py 在common中,所以test.py的当前目录是common(写为.或者./),test.py的上级目录project_h1(写为../)
在test.py中访问日志文件或者写日志问价的路径:../log,意思是project_h1/log
不建议写成project_h1/log ,因为将来如果项目改名,比如project_h1改为project_h2或者别的名字,project_h1/log的写法会出错,而../log不会出错
用例的路径:../excelcase
sql语句文件的路径:../initsqls
路径的拼接
函数read_cases(excel,columns=[]),调用时越简单越好,建议read_cases('login.xlsx')
不建议麻烦的写法read_cases(../excelcase/login.xlsx)(要么函数定义麻烦,要么函数调用麻烦,一般函数定义麻烦好)
定义函数的代码中的excel='../excelcase/'+excel表示将传递来的excel参数左边加上../excelcase,
具体的read_cases('login.xlsx')就会自动变成read_cases('../excelcase/login.xlsx')
例如:
if len(sqlfiles) == 0: # 表示没有给实参,则读取所有sql文件
sqlfiles = [file for file in os.listdir('../initsqls') if file.endswith('.sql')] # 所有sql文件名存入sqlfiles列表
else:
sqlfiles=['../initsqls/'+i for i in sqlfiles]
read_sqls()的调用方式走if分支,sqlfiles结果是../initsqls目录中的所有的sql文件,会自动在每个sql文件名左边加上路径project_h1/initsqls/**.sql
read_sqls(['login.sql','signup.sql])的调用方式走else分支,sqlfiles中的每个文件名左边加上../initsqls(也就是project_h1/initsqls/**.sql)
上面所有出现../的都表示test.py的上级目录
在project_h1中调试test.py文件
import logging, time
def log():
# 创建日志对象
logger = logging.getLogger()
# 禁止日志重复输出 ,每个日志输出一次
if not logger.handlers:
# 指定日志输出级别
logger.setLevel(logging.INFO) # 高于INFO的信息都输出到日志
# 指定日志中的输出格式
# asctime 当前日志时间
# 当前日期时间 - 类型 - 文件名[行号] - 消息
formatter = logging.Formatter('%(asctime)s-%(levelname)s - %(filename)s[%(lineno)d]-%(message)s')
# 创建日志文件
log_file_name = '../log/' + time.strftime('%Y%m%d') + '.log'
# 打开日志文件
logfile = open(log_file_name, 'wb') # w:写 ,b:字节
# 创建处理器
console = logging.StreamHandler() # 流处理器 输出控制台
filehand = logging.FileHandler(log_file_name, encoding='utf-8') # 文件处理器 输出文件
# 指定处理器的日志输出格式
console.setFormatter(formatter)
filehand.setFormatter(formatter)
# 增加处理器到日志对象
logger.addHandler(console)
logger.addHandler(filehand)
# 关闭处理器
console.close()
filehand.close()
return logger
# 调用日志
# log().info('成功的消息')
# log().info('成功22')
# log().error('错误的消息')
# log().error('错误222')
import configparser, os
def entry():
"""
读入口函数:entry()
:return: 返回值是一个元组(被测接口服务器名,数据库服务器名)
"""
try:
conf = configparser.ConfigParser() # 创建对象
conf.read('../conf/entry.ini', encoding='utf-8') # 读取文件
which_server = conf.get('entry', 'which_server') # 读取被测服务器名
which_db = conf.get('entry', 'which_db') # 读取数据库服务器名
log().info('本次测试的接口服务器是' + which_server + ',数据库服务器是' + which_db)
return which_server, which_db
except Exception as e:
log().error('读取入口配置文件entry.ini失败' + e)
# 调试
# entry()
def server_info(): #
"""
读接口服务器信息:包括IP,端口号,组装成http://ip:端口的形式
:return:返回接口地址
"""
try:
which_server = entry()[0]
# 读server.conf中的which_server节点,获得IP、Port
conf = configparser.ConfigParser()
# conf对象中read方法,读取server.conf文件,字符编码设置为utf-8
conf.read('../conf/server.conf', encoding='utf-8')
# 根据exam节点获取键名IP所对应的值,赋值给ip变量
ip = conf.get(which_server, 'IP')
port = conf.get(which_server, 'port')
host = 'http://' + ip + ':' + port
log().info('接口服务器的地址==' + host)
return host
except Exception as e: # Exception是一个关键字,表示所有的异常,e是别名
log().error('接口服务器地址[server.conf]获取失败' + e)
# 调试
# server_info()
def db_info(): # 读db.conf,获得数据库服务器的信息
try: # 异常处理
which_db = entry()[1] # 获得被测数据库服务器名
conf = configparser.ConfigParser()
conf.read('../conf/db.conf', encoding='utf-8')
host = conf.get(which_db, 'host')
port = conf.get(which_db, 'port')
user = conf.get(which_db, 'user')
password = conf.get(which_db, 'password')
db = conf.get(which_db, 'db')
dbinfo = {'host': host, 'port': int(port), "user": user, 'password': password, 'db': db}
log().info('数据库信息==' + str(dbinfo))
return dbinfo
except Exception as e:
log().error('数据库配置文件[db.conf]读取出错' + e)
# 调试
# db_info()
#
#
def read_sqls(sqlfiles=[]): # 读指定sql命令文件的函数,读取到的sql命令存入列表返回;如果没有给实参,则读所有txt中的sql命令
try:
if len(sqlfiles) == 0: # 表示没有给实参,则读取所有sql文件
# 所有.sql文件名存入sqlfiles列表
sqlfiles = [file for file in os.listdir('../initsqls/') if file.endswith('.sql')]
sqlfiles = ['../initsqls/' + i for i in sqlfiles]
sqls = [] # 存sql语句的列表
for file in sqlfiles:
sqlfile = open(file, 'r', encoding='utf-8') # 打开一个文件
for row in sqlfile: # row表示文件中每一行
# 如果这行字符长度大于0 并且 不是以--开头的
if len(row.strip()) > 0 and not row.startswith('--'):
sqls.append(row.strip()) # sql语句中的回车不存入列表
log().info('读取初始化sql语句成功==' + str(sqlfiles))
return sqls
except Exception as e:
log().error('读取初始化sql语句出错' + e)
# 调试
read_sqls()
import pymysql
def conn_db(): # 连接数据库的函数
try:
dbinfo = db_info() # 数据库信息,存入dbinfo字典
conn = pymysql.connect(**dbinfo) # 连接数据库
log().info('连接数据库成功')
return conn
except Exception as e:
log().error('数据库连接出错' + e)
# 调试
# conn_db()
def init_db(sqlfiles=[]): # 执行指定sql文件中的命令
"""
目的:数据初始化
:param sqlfiles: 指定sql文件
:return:
"""
try:
sqls = read_sqls(sqlfiles) # 读取所有需要执行的sql命令
conn = conn_db() # 连接数据库
cursor = conn.cursor() # 创建游标
for sql in sqls:
cursor.execute(sql) # 执行sql语句
conn.commit() # 提交数据到数据库
conn.close()
log().info('初始化数据库成功')
except Exception as e:
log().error('初始化数据库出错')
# 调试
# init_db()
import pandas
def read_cases(excel, columns=[]): # 读取excel用例中的指定列
excel = '../excelcase/' + excel
try:
if len(columns) == 0:
file = pandas.read_excel(excel) # 读取所有列
else:
file = pandas.read_excel(excel, usecols=columns) # 读取指定列
data = file.values.tolist() # 文件数据转为列表
log().info('读取用例文件' + excel + '成功')
return data
except Exception as e:
log().error('读取测试用例文件出错')
# 调试
# read_cases('login.xlsx')
def check_db(case_name, expectsql, dbexpect):
"""
落库检查 检查测试结束后数据的正确性
:param case_name: 用例名称
:param expectsql: sql预期语句
:param dbexpect: 数据库预期结果
:return:
"""
conn = conn_db() # 连接数据库
try:
cursor = conn.cursor() # 创建游标
cursor.execute(expectsql) # 执行sql语句
dbactual = cursor.fetchone()[0] # 取第一行第一列数据
if dbactual == dbexpect:
log().info(case_name + '==落库检查==通过')
else:
log().warning(case_name + '==落库检查==失败==预期行数:' + str(dbexpect) + ',实际行数:' + str(dbactual))
except Exception as e:
log().error('落库检查出错' + e)
# 调试
# check_db('数据库行数', 'select count(*) from user', 5)
import requests
def post(address, argument, case_name, expect):
"""
:param address: 接口地址
:param argument: 请求参数
:param case_name: 用例名称
:param expect: 预期结果
:return:
"""
try:
# 发送请求 判断响应结果的正确性
res = requests.post(url=address, data=argument)
if 'text/html' in res.headers['Content-Type']: # 表示响应内容的类型是字符串
actual = res.text
# 结果比对
if expect in actual:
log().info('比对登录接口返回==' + case_name + '==通过')
else:
log().warning('比对登录接口返回==' + case_name + '==失败==预期结果:' + expect + ',实际结果:' + actual)
elif 'application/json' in res.headers['Content-Type']:
actual = res.json() # 实际结果(字典)
# 比对接口返回结果
if expect == actual:
log().info("比对注册接口返回==" + case_name + '==通过')
else:
log().warning('比对注册接口返回==' + case_name + '==失败==预期结果:' + str(expect) + ',实际结果:' + str(actual))
else: # xml
pass # 暂时不写代码
except Exception as e:
log().error('发送数据,比对响应结果出错')
def test_login():
"""
测试登录接口
:return:
"""
# 数据库初始化
init_db(['login.sql'])
# 读测试用例数据
cases = read_cases('login.xlsx') # 用例:用例名0、参数数据1、预期结果2
# 读接口服务器地址
host = server_info()
address = host + '/exam/login/'
try:
# 发送请求
for case in cases: # case代表一行用例
case_name = case[0] # 第0列,是用例名称
argument = eval(case[1]) # 要发送的参数
expect = case[2] # 预期接口的返回结果
post(address, argument, case_name, expect)
except Exception as e:
log().error('登录接口测试出错' + e)
# 调试
# test_login()
def test_signup():
"""
测试注册接口
:return:
"""
# 数据库初始化
init_db(['signup.sql'])
# 读测试用例
cases = read_cases('signup.xlsx')
# 接口地址
host = server_info()
address = host + '/exam/signup/'
try:
# 发送请求
for case in cases:
case_name = case[0] # 用例名称
argument = eval(case[1]) # 参数
expect = eval(case[2]) # 预期接口返回结果
sql = case[3] # 预期sql
dbexpect = case[4] # 预期数据库行数
# 发送请求
post(address, argument, case_name, expect)
# 落库检查
check_db(case_name, sql, dbexpect)
except Exception as e:
log().error('注册接口测试出错' + e)
# 调试
test_signup()
本文来自博客园,作者:暄总-tester,转载请注明原文链接:https://www.cnblogs.com/sean-test/p/15519996.html

浙公网安备 33010602011771号