项目实战(二)

  • Excel封装
import openpyxl
from common.logger_handler import logger


class ExcelHandler:
"""
Excel表格数据操作
"""

def __init__(self, f_path):
"""
初始化ExcelHandler的实列
:param f_path:excel文件路径
"""
self.file_path = f_path

def read(self, sheet_name):
"""
从excel文件中读取数据
:param sheet_name: sheet表单的名称
:return: 列表类型数据
"""
try:
work_book = openpyxl.open(self.file_path)
except FileNotFoundError as e:
logger.error("文件打开失败{}".format(e))
work_sheet = work_book[sheet_name]
sheet_data = list(work_sheet.values)
sheet_head = sheet_data[0]
sheet_data_dict_list = []
for data in sheet_data[1:]:
data_dict = dict(zip(sheet_head, data))
sheet_data_dict_list.append(data_dict)
return sheet_data_dict_list

def write(self,sheet_name,data,row,column):
"""
向表单中写入数据
:param sheet_name: 表单名
:param data: 需要写入的数据
:param row: 需要操作的行
:param column: 需要操作的列
:return: NULL
"""
try:
work_book = openpyxl.load_workbook(self.file_path)
except FileNotFoundError as e:
logger.error("文件打开失败{}".format(e))
work_sheet = work_book[sheet_name]
work_sheet.cell(row=row, column=column).value = data
# work_book保存并关闭
work_book.save(self.file_path)
work_book.close()
  • logger封装

logger封装为函数

import logging
import os


def get_logger(name="root",
logger_level="DEBUG",
handler_level="DEBUG",
file=None,
file_handler_level="INFO",
fmt_str="time:%(asctime)s-%(levelname)s:%(name)s:%(message)s--%(filename)s:%(lineno)d"):
# 获取收集器并设置日志等级
logger = logging.getLogger(name)
logger.setLevel(logger_level)

# 日志格式设置
fmt = logging.Formatter(fmt_str)

# 获得流处理器并设置日志等级和设置日志格式
handler = logging.StreamHandler()
handler.setLevel(handler_level)
handler.setFormatter(fmt)

# 将处理器
logger.addHandler(handler)

# 获得文件处理器并设置日志等级和设置日志格式
if file:
file_handler = logging.FileHandler(file, encoding="utf-8")
file_handler.setLevel(file_handler_level)
file_handler.setFormatter(fmt)
# 将处理器添加到收集器上
logger.addHandler(file_handler)

return logger
logger封装为类
import logging


class MyLogger(logging.Logger):

def __init__(self,
name='root',
logger_level='DEBUG',
stream_handler_level='DEBUG',
file=None,
file_handler_level='INFO',
fmt_str="time:%(asctime)s--%(levelname)s:%(name)s:%(message)s--%(filename)s---%(lineno)s"):

# 获取日志收集器 logger
super().__init__(name, logger_level)
# self == 收集器
# self.setLevel(logger_level)

# "time:%(asctime)s--%(levelname)s:%(name)s:%(message)s--%(filename)s---%(lineno)s"
fmt = logging.Formatter(fmt_str)
# 日志处理器
handler = logging.StreamHandler()
handler.setLevel(stream_handler_level)
self.addHandler(handler)
handler.setFormatter(fmt)
# 文件处理器
if file:
file_handler = logging.FileHandler(file, encoding="utf-8")
file_handler.setLevel(file_handler_level)
self.addHandler(file_handler)
file_handler.setFormatter(fmt)
  • yaml文件处理封装
import yaml
import os
from common.path_handler import get_dirname_path


def get_yaml(file):
"""获取yaml配置文件中的数据"""
with open(file, encoding="utf-8") as f:
data = yaml.load(f, Loader=yaml.SafeLoader)
return data


# 获取yaml文件数据
yaml_file = os.path.join(get_dirname_path("config"), "api.yaml")
yaml_data = get_yaml(yaml_file)
  • 工具封装

随机生成手机号码:

from faker import Faker


def generator_new_phone():
"""生成随机手机号码"""
# 创建一个指定中文的Faker对象
fk = Faker(locale="zh_CN")
# 获得随机手机号码
phone = fk.phone_number()
return phone
  • 数据库类的封装
import pymysql
from common.logger_handler import get_logger
from common.member import Member
from common.tool_handler import generator_new_phone


class DatabaseHandler:
"""数据库增删改查的封装"""

def __init__(self, host, port, user, password):
"""
初始化方法,初始化实例属性,供数据库连接的相关参数
:param host: 数据库连接的主机名
:param port: 数据库连接的端口号
:param user: 数据库连接的用户名
:param password: 数据库连接的密码
"""
self.host = host
self.port = port
self.user = user
self.password = password

def get_db_conn(self, database_name):
"""
获得数据库连接
:param database_name: 连接的数据库名
:return: Connection对象
"""
try:
conn = pymysql.Connection(host=self.host,
port=self.port,
user=self.user,
password=self.password,
database=database_name,
charset="utf8") # charset在mysql中是utf8,不是utf-8
except pymysql.Error as e:
logger = get_logger()
logger.error("connect mysql with error:{}".format(e))

return conn

def get_member_info_by_memberId(self, db, table_name, memberId):
"""
通过memberId获取用户信息
:param db: 操作的数据库
:param table_name: 操作的表
:param memberId: 用户id
:return: 用户信息数据,元组类型
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "SELECT * FROM {} WHERE id=%s".format(table_name)
cursor.execute(sql, memberId)
member_data = cursor.fetchall()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return member_data

def get_member_info_by_phone(self, db, table_name, phone):
"""
通过手机号码获得用户信息
:param db: 数据库名
:param table_name: 表名
:param phone: 用户手机号码
:return: 用户数据,元组类型
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "SELECT * FROM {} WHERE mobile_phone=%s".format(table_name)
cursor.execute(sql, phone)
member_data = cursor.fetchall()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return member_data

def get_member_info(self, db, table_name):
"""
获取最新建立100位的用户信息(因为数据库用户太多,查询语句将所有的信息查出太耗时间)
:param db: 数据库名
:param table_name: 表名
:return: 用户信息,元组类型
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "SELECT * FROM {} ORDER BY id DESC LIMIT 100".format(table_name)
cursor.execute(sql)
member_data = cursor.fetchall()
# 关闭连接
cursor.close()
# 关闭连接
conn.close()
return member_data

def insert_member(self, db, table_name, member):
"""
新增一个用户
:param member: 用户对象
:return: 影响的行数
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "INSERT INTO member(reg_name,pwd,mobile_phone,type,leave_amount) VALUES(%s,%s,%s,%s,%s)"
member_data = [member.reg_name, member.password, member.mobile_phone, member.m_type, member.leave_amount]
flag = cursor.execute(sql, member_data)
# 判断是否插入成功
if flag:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return True
else:
conn.commit()
cursor.close()
conn.close()
return False

def delete_member_by_memberId(self, db, table_name, memberId):
"""
根据memberId删除对应的member数据
:param self:数据库实列
:param db:数据库
:param table_name:表名
:param memberId:用户id
:return:bool
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "DELETE FROM {} WHERE id=%s".format(table_name)
flag = cursor.execute(sql, memberId)
if flag:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return True
else:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return False

def delete_member_by_phone(self, db, table_name, phone):
"""
根据memberId删除对应的member数据
:param self:数据库实列
:param db:数据库名
:param table_name:表名
:param phone:用户手机号码
:return:bool
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "DELETE FROM {} WHERE mobile_phone=%s".format(table_name)
flag = cursor.execute(sql, phone)
# 判断是否删除
if flag:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return True
else:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return False

def update_reg_name_by_memberId(self, db, table_name, memberId, data):
"""
根据memberId更新对应的member数据
:param self:数据库实列
:param db:数据库
:param table_name:表名
:param memberId:用户id
:param data:更新数据的内容
:return:bool
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "UPDATE {} SET reg_name=%s WHERE id=%s".format(table_name)
flag = cursor.execute(sql, [data, memberId])
# 判断是否更新
if flag:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return True
else:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return False

def update_reg_name_by_phone(self, db, table_name, phone, data):
"""
根据memberId更新对应的member数据
:param self:数据库实列
:param db:数据库
:param table_name:表名
:param phone:用户手机号码
:param data:更新数据的内容
:return:bool
"""
# 获得数据库连接
conn = self.get_db_conn(db)
# 获得游标
cursor = conn.cursor()
sql = "UPDATE {} SET reg_name=%s WHERE mobile_phone=%s".format(table_name)
flag = cursor.execute(sql, [data, phone])
# 判断是否更新
if flag:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return True
else:
# 对数据的更新需要进行提交才能生效
conn.commit()
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
return False
工具:随机生成手机号码
from faker import Faker
import random


def generator_new_phone():
"""生成随机手机号码"""
# 创建一个指定中文的Faker对象
fk = Faker(locale="zh_CN")
# 获得随机手机号码
phone = fk.phone_number()
return phone


# def generator_new_phone():
# mobile_phone = random.choice(["13", "19", "17"])
# for i in range(9):
# mobile_phone += str(random.randint(0, 9))
# return str(mobile_phone)
框架优化:
1、excel封装,读写,读可读取初始,写数据,可将pass fail以及接口返回的数据写入excel的测试用例中
2、提取url中的host放入配置文件yaml中
3、将数据库连接参数host、port、user、password都放入配置文件yaml中
  数据库封装:
    数据库操作:
      1、建立连接pymysql.Connection()
      2、获取游标conn.cursor()
      3、通过游标执行sql语句cursor.execute()
      4、通过游标获得结果cursor.fetchall()/cursor.fetchone()
      5、数据提交conn.commit()对数据库表新增、删除、修改数据需要提交才能生效
      6、关闭游标cursor.close()
      7、关闭连接conn.close()
4、注册成功的用例,第一次可注册成功,之后无论怎么运行都用例都不会通过,因为手机号码已存在,如果自动生成新的手机号码?使用Faker随机生成手机号码。
  读取parameters数据,如果存在特定字符串如:"#new_phone#",if语句判断"#new_phone#" in str中,替换,字符串.replace("#new_phone#",phone)
5、
总结:踩坑的地方:
1、日志文件封装后使用出现控制台可以正常打印日志,但是日志文件没有创建:出现这个问题,肯定是日志文件创建有问题,由于不太注意在进行if判断的时候写了,if not file:因为file的默认值本身就是None所以,
当传入file后if判断就为False了,故没有创建日志文件
2、html测试报告中失败的用例没有打印详细的错误信息,原因是pytest.main()中添加了参数 -s,添加该参数后不会捕获信息,是调试信息,都输出到控制台了
3、db封装特别要注意:新增、删除、更新数据一定要记得conn.commit不然不会生效;数据操作后需要cursor.close(),conn.close()
4、sql语句的str中表名不能以参数的形式传入cursor.execute(sql,[variable1,variable2])中,否会报sql语法错误,需要将表名添加到字符串sql中,sql.format(table_name)添加到字符串sql中。
封装数据库的时候,可以直接传入完整的sql语句,这样简洁方便很多。
5、游标的fetchone和fetchall问题,同一个游标对象在操作过fetchone后再次操作fetchall得出的结果会是减少一条记录,并不会得到全部的数据,原因:游标的位置问题,游标的移动是从第一行往下读的,同一个游标对象操作fetchone
后读取一条数据,游标位置移动到第二行,故此时再去fetchall获得就是从第二行开始到末尾的数据,所以当需要同时操作fetchone和fetchall的时候操作完fetchone后需要将游标关闭再重新打开再去操作fetchall。
posted @ 2021-01-27 11:41  %女王%  阅读(84)  评论(0编辑  收藏  举报