Log类处理
log类主要是对全项目代码做log打印的一段代码,
if __name__ == "__main__":
log = MyLog.get_log()
logger = log.get_logger()
logger.debug("test debug")
logger.info("test info")
2024-07-13 15:41:08,034 - root - INFO - test info
在代码中用到了logger模块,实例logger来创建handler。常见的Handler就是我们常用的I/O操作, 类似于我们的print()以及file.write(),那么handler是如何记录信息的了。通过下面的简单代码应该会有所了解。具体步骤如下: 1)创建logger 2)创建handler 3)定义formatter 4)给handler添加formatter 5)给logger添加handler'''
class Log:
def __init__(self):
global logPath, resultPath, proDir
proDir = readConfig.proDir
resultPath = os.path.join(proDir, "result")
if not os.path.exists(resultPath):
os.mkdir(resultPath)
logPath = os.path.join(resultPath, str(datetime.now().strftime("%Y%m%d%H%M%S")))
if not os.path.exists(logPath):
os.mkdir(logPath)
self.logger = logging.getLogger()
self.logger.setLevel(logging.INFO)
# defined handler
handler = logging.FileHandler(os.path.join(logPath, "output.log"))
# defined formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
在log类中其实也有存在常用的一些封装,主要是针对一些基本的通用的日志信息简单封装城一个方法,方便后续调用,示例如下:
def get_logger(self):
"""
get logger
:return:
"""
return self.logger
def build_start_line(self, case_no):
"""
write start line
:return:
"""
self.logger.info("--------" + case_no + " START--------")
def build_end_line(self, case_no):
"""
write end line
:return:
"""
self.logger.info("--------" + case_no + " END--------")
def build_case_line(self, case_name, msg):
"""
write test case line
:param case_name:
:param code:
:param msg:
:return:
"""
self.logger.info(case_name+"----msg:"+msg)
日志打印和存储到日志文件,涉及I/O操作,往往面临着并发操作,为了方便日志存储不阻塞。需要添加线程锁的操作:
class MyLog:
log = None
mutex = threading.Lock()
def __init__(self):
pass
@staticmethod
def get_log():
if MyLog.log is None:
MyLog.mutex.acquire()
# 調用上面提到的公共log類
MyLog.log = Log()
MyLog.mutex.release()
return MyLog.log
请求基本类封装
1、接口cookies、token获取
在接口状态保持这一章节中,我们提到了通过接口的方式去保持登录状态,其中通过接口的方式获取了返回的cookies和token,获取以后保存到ini配置文件中,文件内容如下:
通用类里面我们通过这两个函数来保存获取的cookies和token
当我们要调用文件里面的内容时,首先是初始化内容,实例化类。
login_xls = common.get_xls("userCase.xlsx", "login")
localReadConfig = readConfig.ReadConfig()
configHttp = ConfigHttp.ConfigHttp()
info = {}
通过读取配置内容来获取需要的cookies或者token,这里用到set_headers()函数。
我们来看一下set_headers()函数,此函数返回了HEADERS下的name,name如果传参cookies就是获取的cookies。
# get visitor token
if self.token == '0':
token = localReadConfig.get_headers("token_v")
elif self.token == '1':
token = None
def get_headers(self, name):
value = self.cf.get("HEADERS", name)
return value
2、接口url拼接
接口测试时接口是经常用到的一个地方,接口通俗点就是url,一个链接。那么什么是链接呢?
举个例子随意一个链接看看:baike.baidu.com/item/url/11…
这段代码是是从xml里获取url,文件存放在testfile文件夹下。
Url获取后我们需要和域名和协议拼接在一起组成一个完整的链接,这里为什么要如此麻烦分三部分来合成呢,主要考虑到的是协议和域名一般是变动不大的。当然你也可以采取直接获取的方式,如脚本用例的yaml里就直接有地址的
而Excel文件里采用和xml一样的方式需要拼接。
拼接的规则在之前的接口配置类里面就写好了方法,如下所示:
def set_url(self, host,url):
"""
set url
:param: interface url
:return:
"""
#self.url = scheme+'://'+host+url
self.url = host+url
return self.url
3、接口返回结果比对
完成了以上设置后,接下来的就是如何确认,接口返回的结果是对的,跟我们用例期望结果有什么差距。这里就需要接口通用类里的一个比较结果的方法来判定。 代码分为对全部内容的检测和对单一的key值检测,在全部内容了用分字典类型和列表类型的检测,随后是循环读取判定的数据,其中内部调用了本身的这个函数和一个外部自定义的函数。 外部自定义的函数这里我只是随便的做了一个示例,实际可以写的复杂一点。
def checkResult(self):
"""
check test result
:return:
"""
self.info = self.return_json.json()
# show return message
common.show_return_msg(self.return_json)
if self.result == '0':
email = common.get_value_from_return_json(self.info, 'member', 'email')
self.assertEqual(self.info['code'], self.code)
self.assertEqual(self.info['msg'], self.msg)
self.assertEqual(email, self.email)
if self.result == '1':
self.assertEqual(self.info['code'], self.code)
self.assertEqual(self.info['msg'], self.msg)
如何读取ini文件
readConfig.py这个文件主要是对获取配置文件ini的操作方法进行封装,有利于我们读取配置内容,实际代码也是参考源码来实现的。常见的ini文件内容如下图所示,层级分明:
具体实现操作的Python源码如下,根据方法大家可以自行扩展。
import os
import codecs
import configparser
proDir = os.path.split(os.path.realpath(__file__))[0]
print(proDir)
configPath = os.path.join(proDir, "config.ini")
class ReadConfig:
def __init__(self):
fd = open(configPath)
data = fd.read()
# remove BOM
if data[:3] == codecs.BOM_UTF8:
data = data[3:]
file = codecs.open(configPath, "w")
file.write(data)
file.close()
fd.close()
self.cf = configparser.ConfigParser()
self.cf.read(configPath)
def get_email(self, name):
value = self.cf.get("EMAIL", name)
return value
def get_http(self, name):
value = self.cf.get("HTTP", name)
return value
def get_headers(self, name):
value = self.cf.get("HEADERS", name)
return value
def set_headers(self, name, value):
self.cf.set("HEADERS", name, value)
with open(configPath, 'w+') as f:
self.cf.write(f)
def get_url(self, name):
value = self.cf.get("URL", name)
return value
def get_db(self, name):
value = self.cf.get("DATABASE", name)
return value
def get_user_info(self,info):
value = self.cf.get("USERINFO",info)
return value
在上面的代码中我们导入了configparser模块,此模块主要用于解析ini文件,对文件内容进行读取和修改。ConfigParser模块以ConfigParser类为例,其操作基本分为三个步骤:
1)实例对象,初始化操作;
2)读取配置文件ini;
3)写入配置文件
ConfigParser模块中常用到的方法有:
很容易可以看到在代码初始化中有这么一个操作,去除BOM头。为什么要执行此操作呢,因为ini文件的保存格式是utf-8,这个是Windows系统默认的保存方法,在代码执行时对数据的读取中需要将里面的数据转为ASCII码,但是如果出现其他中文,或者特殊字符转码时会出现乱码。如何才能不乱码呢?这里我们需要在文件的BOM头做修改,这样下次电脑系统识别此文件时就会以ASCII码来读取了,所以我们用自带的fp的方法打开文件去除BOM头后,在保存一次即可
文章原创首发于微信公众号 软件测试微课堂,更多内容欢迎关注微信公众号查看