使用函数与类封装框架

面向对象的编程过程

1.创建类
构造方法(可以省略,要写的话必须写__init__(self),一般用于定义成员变量)
成员方法(可以省略,名字自己起,self参数必须写,而且写在第一个,用于处理数据)
2.创建对象
对象名 = 类名() -- 实例化
3.调用方法
对象名.成员方法()
import logging, time, configparser, pymysql, os, pandas, requests


def log():
"""
日志函数
:return:
"""
# 创建日志对象
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


class Entry: # 入口类 建议类名首字母大写
def __init__(self):
"""
构造方法
:return: 返回值是一个元组(被测接口服务器名,数据库服务器名)
"""
try:
# 创建对象
conf = configparser.ConfigParser()
# 读取文件
conf.read('../conf/entry.ini', encoding='utf-8')
# 读取接口服务器名 -- 通过节点名 找到服务器名字 成员变量
self.which_server = conf.get('entry', 'which_server')
# 读取接口数据库服务器名 -- 通过节点名 找到数据库名字 成员变量
self.which_db = conf.get('entry', 'which_db')
except Exception as e:
log().error('读取入口配置文件entry.ini失败' + e)

def get_which_server(self): # 成员方法 获得接口服务器方法
log().info('本次测试的接口服务器是:' + self.which_server)
return self.which_server

def get_which_db(self): # 成员方法 获得数据库名字
log().info('本次测试的数据库服务器是:' + self.which_db)
return self.which_db


# 调式检查代码
# e = Entry() # 创建一个对象,自动执行__init__中的程序代码,括号不能省略
# print(e.which_server, e.which_db) # 很少这样运行
# print(e.get_which_server(), e.get_which_db()) # 更多的使用方法
# 真正运用时,存储返回值,但是很少写print()
# 使用方法最多是调用成员方法
# server = e.get_which_server()
# db = e.get_which_db()
# print('接口服务器名是:' + server + ',数据库服务器名是:' + db)

class ServerConf: # 获取接口服务器的地址
def __init__(self):
try:
# 获得要测试的接口服务器的名字
which_server = Entry().get_which_server()
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')
self.host = 'http://' + ip + ':' + port
except Exception as e: # Exception是一个关键字,表示所有的异常,e是别名
log().error('接口服务器地址[server.conf]获取失败' + e)

def get_host(self):
log().info('接口服务器地址==' + self.host)
return self.host


# 调试
# s = ServerConf() # s可以使用host,但是不能使用which_server,conf,ip,port
# print(s.get_host())# 调用成员方法,获得被测接口服务器的url地址

class DB:
def __init__(self):
"""
读取db.conf文件,获得数据库服务器的信息
返回内容是 数据库服务器信息
"""
try: # 异常处理
# 获得测试要用的数据库服务器的节点名
which_db = Entry().get_which_db()
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')
self.dbinfo = {'host': host, 'port': int(port), "user": user, 'password': password, 'db': db}
log().info('数据库信息==' + str(self.dbinfo))
except Exception as e:
log().error('数据库配置文件[db.conf]读取出错' + e)

def conn_db(self): # 连接数据库的函数
try:
# conn = pymysql.connect(host='192.168.175.128',port=3306,user='root',password='123456',db='exam')
conn = pymysql.connect(**self.dbinfo) # 连接数据库
log().info('连接数据库成功')
return conn
except Exception as e:
log().error('数据库连接出错' + e)

# 调试
# 创建对象,自动调用DB中的init的函数或方法
# DB()
# 调用DB()对象的方法conn_db连接数据库
# DB().conn_db()
# 返回为True表示成功
# print(DB().conn_db().open)

def get_sqlfiles(sqlf, sqlfiles=[]): # sqlfiles默认参数,要给参数只能给列表,要么不给
try:
if len(sqlfiles) == 0: # 表示没有给实参,则读取所有sql文件
sqlfiles = [file for file in os.listdir('../initsqls/') if
file.endswith('.sql')] # 所有.sql文件名存入sqlfiles列表
sqlfiles = ['../initsqls/' + i for i in sqlfiles]
log().info('获得sql文件名' + str(sqlfiles) + '列表成功')
return sqlfiles
except Exception as e:
log().error('获得sql文件名列表失败')


# 调试
# 只写文件名 不用写路径 程序自动加路径
# print(DB().get_sqlfiles(['login.sql']))

# 读取带.sql文件扩展名文件中的sql语句
def read_sqls(self, sqlfiles=[]):
sqlfiles = self.get_sqlfiles(sqlfiles) # 获得带路径的文件名列表
sqls = [] # 存sql语句的列表
try:
for sqlfile in sqlfiles: # sqlfile表示每一个sql语句文件
sfile = open(sqlfile, 'r', encoding='utf-8') # 打开文件
for sql in sfile: # sql是从sfile文件中获得每一行
# 如果这行字符长度大于0 并且 不是以--开头的
if len(sql.strip()) > 0 and not sql.startswith('--'):
sqls.append(sql.strip()) # sql语句中的回车不存入列表
log().info('读取初始化sql语句成功==' + str(sqlfiles))
return sqls
except Exception as e:
log().error('读取初始化sql语句出错' + e)


# 调试
# print(DB().read_sqls(['login.sql']))
# print(DB().read_sqls())

def init_db(self,sqlfiles=[]): # #执行sqlfiles文件中的sql语句,初始化数据库
"""
目的:数据初始化
:param sqlfiles: 指定sql文件
:return:
"""
sqls = self.read_sqls(sqlfiles) # 读取所有需要执行的sql命令
conn = self.conn_db() # 连接数据库
cursor = conn.cursor() # 创建游标
try:
for sql in sqls:
cursor.execute(sql) # 只能一条一条的执行,结果暂存到cursor中
conn.commit() # 提交数据到数据库
conn.close()
log().info('初始化数据库成功')
except Exception as e:
log().error('初始化数据库出错')

# 调试
# DB().init_db(['login.sql'])
# DB().init_db()
def check_db(self,case_name,expectsql,argument,expect_db_rows):
"""
:param case_name: 用例名称
:param expectsql: 预期sql语句
:param argument: 接口参数
:param expect_db_rows: 预期行数
:return:
"""
conn = self.conn_db() # 连接数据库
try:
cursor = conn.cursor() # 创建游标
cursor.execute(expectsql) # 执行sql语句
dbactual = cursor.fetchone()[0] # 取第一行第一列数据
if dbactual == expect_db_rows:
log().info(case_name + '==落库检查==通过')
else:
log().warning(case_name + '==落库检查==失败==预期行数:' + str(expect_db_rows) + ',实际行数:' + str(dbactual))
except Exception as e:
log().error('落库检查出错' + e)

# 调试
DB().check_db('测试','select count(*) from user',{'username':'test01'},4)

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('读取测试用例文件出错')


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:
"""
# 数据库初始化
DB().init_db(['login.sql'])
# 读测试用例数据
cases = read_cases('login.xlsx') # 用例:用例名0、参数数据1、预期结果2
# 读接口服务器地址
host = ServerConf().get_host()
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:
"""
# 数据库初始化
DB().init_db(['signup.sql'])
# 读测试用例
cases = read_cases('signup.xlsx')
# 接口地址
host = ServerConf().get_host()
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)
# 落库检查
DB().check_db(case_name,sql,argument,dbexpect)
except Exception as e:
log().error('注册接口测试出错' + e)


# 调试
test_signup()

作者:暄总-tester