【python3接口测试框架】公用类的深度解析

61 阅读6分钟

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头后,在保存一次即可

文章原创首发于微信公众号 软件测试微课堂,更多内容欢迎关注微信公众号查看