接口框架辅助类的深度解析(上篇)

源码数据库配置揭秘

在本次接口框架实战项目中的示例文件为configDB.py,放在commomsrc文件夹内。接下来我们来分析一下文件内容。文件头部是导入一些类,pymysql模块、readConfig类、log类。

javascript 复制代码
import pymysql
import readConfig as readConfig
from commonsrc.Log import MyLog as Log

localReadConfig = readConfig.ReadConfig()

正文部分定义了一个大类MyDB,看到全局变量标识符global,就会知道后面的都是全局变量,然后通过readconfig类读取ini的文件内容,配置数据库连接基本验证信息。

css 复制代码
class MyDB:
    global host, username, password, port, database, config
    host = localReadConfig.get_db("host")
    username = localReadConfig.get_db("username")
    password = localReadConfig.get_db("password")
    port = localReadConfig.get_db("port")
    database = localReadConfig.get_db("database")
    config = {
        'host': str(host),
        'user': username,
        'passwd': password,
        'port': int(port),
        'db': database
    }

接下来的初始化方法__init__里初始了log类,数据库,数据库游标。真正的正文内容依次为连接数据库,执行sql语句,获取数据库所有查询内容,获取一条内容,关闭数据库。

python 复制代码
   def __init__(self):
        self.log = Log.get_log()
        self.logger = self.log.get_logger()
        self.db = None
        self.cursor = None

    def connectDB(self):
        """
        connect to database
        :return:
        """
        try:
            # connect to DB
            self.db = pymysql.connect(**config)
            # create cursor
            self.cursor = self.db.cursor()
            print("Connect DB successfully!")
        except ConnectionError as ex:
            self.logger.error(str(ex))

    def executeSQL(self, sql, params):
        """
        execute sql
        :param sql:
        :return:
        """
        self.connectDB()
        # executing sql
        self.cursor.execute(sql, params)
        # executing by committing to DB
        self.db.commit()
        return self.cursor

    def get_all(self, cursor):
        """
        get all result after execute sql
        :param cursor:
        :return:
        """
        value = cursor.fetchall()
        return value

    def get_one(self, cursor):
        """
        get one result after execute sql
        :param cursor:
        :return:
        """
        value = cursor.fetchone()
        return value

    def closeDB(self):
        """
        close database
        :return:
        """
        self.db.close()
        print("Database closed!")

连接数据库这一步,py文件定义的方法名为connectDB,语句self.db = pymysql.connect(**config)这里我们参数输入的是动态参数,指向之前配置的config里,然后产生游标,游标的主要作用时确认你当前操作位置是在哪里,数据库的哪里。

python 复制代码
    def connectDB(self):
        """
        connect to database
        :return:
        """
        try:
            # connect to DB
            self.db = pymysql.connect(**config)
            # create cursor
            self.cursor = self.db.cursor()
            print("Connect DB successfully!")
        except ConnectionError as ex:
            self.logger.error(str(ex))

执行数据库sql语句的方法里主要是先调用上面的连接数据库方法,随后执行数据库sql语句运用到了execute方法,这里大家需要注意一定要有提交方法commit。只有提交了才会执行sql语句。

python 复制代码
    def executeSQL(self, sql, params):
        """
        execute sql
        :param sql:
        :return:
        """
        self.connectDB()
        # executing sql
        self.cursor.execute(sql, params)
        # executing by committing to DB 注意此处的事务提交
        self.db.commit()
        return self.cursor

如何获取查询出来的所有数据呢,这里直接是用到了cursor.fetchall()方法获取所有数据,cursor.fetchone()方法获取一条数据。

python 复制代码
    def get_all(self, cursor):
        """
        get all result after execute sql
        :param cursor:
        :return:
        """
        value = cursor.fetchall()
        return value

    def get_one(self, cursor):
        """
        get one result after execute sql
        :param cursor:
        :return:
        """
        value = cursor.fetchone()
        return value

关闭数据库这一块,我们主要是为了安全考虑,就像打开文件的open方法一样,吸管吸的打开了就关闭掉而已。

python 复制代码
    def closeDB(self):
        """
        close database
        :return:
        """
        self.db.close()
        print("Database closed!")

邮件配置源码

在本次接口框架实战项目中的示例文件为configEmail.py,放在commomsrc文件夹内。接下来我们来分析一下文件内容。文件的头部导入的是os、smtplib、email库,zipfile方法等。

javascript 复制代码
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from datetime import datetime
import threading
import readConfig as readConfig
from commonsrc.Log import MyLog
import zipfile
import glob


localReadConfig = readConfig.ReadConfig()

正文部分是有两个大类一个是Email,一个是MyEmail。在Email类里,我们有这么几步操作,处理了邮件的配置信息、邮件基本头配置、邮件文本基本配置、邮件图片基本配置,邮件文件基本配置、核对需要发送的东西、邮件发送。

在初始化__init__文件里,我们对基本的身份验证配置信息做了配置,log的打印、发送时间等都有定义,定义的时候大部分采用了ini文件配置的信息。

ini 复制代码
    def __init__(self):
        global host, user, password, port, sender, title
        host = localReadConfig.get_email("mail_host")
        user = localReadConfig.get_email("mail_user")
        password = localReadConfig.get_email("mail_pass")
        port = localReadConfig.get_email("mail_port")
        sender = localReadConfig.get_email("sender")
        title = localReadConfig.get_email("subject")
        # content = localReadConfig.get_email("content")

        # get receiver list
        self.value = localReadConfig.get_email("receiver")
        self.receiver = []
        for n in str(self.value).split("/"):
            self.receiver.append(n)

        # defined email subject
        date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.subject = "接口测试报告" + " " + date

        self.log = MyLog.get_log()
        self.logger = self.log.get_logger()
        self.msg = MIMEMultipart('related')

邮件基本头配置,这里写的比较简单就是基本的发送人和接受人。其中self.msg表示的是邮件类型是可以发送超文本和内嵌资源的类型。

python 复制代码
    def config_header(self):
        """
        defined email header include subject, sender and receiver
        :return:
        """
        self.msg['subject'] = self.subject
        self.msg['from'] = sender
        self.msg['to'] = ";".join(self.receiver)

邮件文本基本配置,先打开在testFile文件夹里的,email的html样式文本。然后通过MIMEText()方法对文本进行转换为html格式,方法的最后一行是邮件图片基本配置。这里给大家留一个疑惑,这个email的html样式是不是可以自己自定义,自己找其他的样式也可以。里面有什么是要写代码关联的吗?

python 复制代码
    def config_content(self):
        """
        write the content of email
        :return:
        """
        f = open(os.path.join(readConfig.proDir, 'testFile', 'emailStyle.txt'))
        content = f.read()
        f.close()
        content_plain = MIMEText(content, 'html', 'UTF-8')
        self.msg.attach(content_plain)
        self.config_image()

邮件图片基本配置,同样是在testFile里找到对应的图片,通过MIMEImage()方法读取图片,下面我们仔细看发现这里有定义id的这么一个操作,这个是啥情况呢?在这里定义了两张图片image1和image2。这是因为在html的样式里有用到这个图片,而他们关联的唯一标识就是和。

python 复制代码
    def config_image(self):
        """
        config image that be used by content
        :return:
        """
        # defined image path
        image1_path = os.path.join(readConfig.proDir, 'testFile', 'img', 'logo.png')
        fp1 = open(image1_path, 'rb')
        msgImage1 = MIMEImage(fp1.read())
        # self.msg.attach(msgImage1)
        fp1.close()

        # defined image id
        msgImage1.add_header('Content-ID', '<image1>')
        self.msg.attach(msgImage1)

        image2_path = os.path.join(readConfig.proDir, 'testFile', 'img', 'image2.png')
        fp2 = open(image2_path, 'rb')
        msgImage2 = MIMEImage(fp2.read())
        # self.msg.attach(msgImage2)
        fp2.close()

        # defined image id
        msgImage2.add_header('Content-ID', '<image2>')
        self.msg.attach(msgImage2)

核对需要发送的东西,这里我们代码写的是核对是否在目录下存在html的报告,实际可以核对更多的东西,代码逻辑是一样的。这里需要注意的是os.path.isfile()方法,如果检测目录含中文字符,始终返回的结果是false,在括号里可以加个os.path.join(path,files[1]).

python 复制代码
    def check_file(self):
        """
        check test report
        :return:
        """
        reportpath = self.log.get_report_path()
        #reportpath = unicode(reportpath, "utf8")
        if os.path.isfile(os.path.join(reportpath)) and not os.stat(reportpath) == 0:
            return True
        else:
            return True

邮件发送,这里一个一个的运行上面几个方法,email是python自带构造文件内容的一个模块,而这里smtp则是发送邮件的一个python自带模块。

python 复制代码
    def send_email(self):
        """
        send email
        :return:
        """
        self.config_header()
        self.config_content()
        self.config_file()
        try:
            smtp = smtplib.SMTP()
            smtp.connect(host)
            smtp.login(user, password)
            smtp.sendmail(sender, self.receiver, self.msg.as_string())
            smtp.quit()
            self.logger.info("The test report has send to developer by email.")
        except Exception as ex:
            self.logger.error(str(ex))

这个类是一个静态方法,通过异步发送邮件,防止线程阻塞。先加载线程锁,复制给第一个大类后在释放线程锁。达到了异步发送邮件的一个效果。

python 复制代码
class MyEmail:
    email = None
    mutex = threading.RLock()

    def __init__(self):
        pass

    @staticmethod
    def get_email():

        if MyEmail.email is None:
            MyEmail.mutex.acquire()
            MyEmail.email = Email()
            MyEmail.mutex.release()
        return MyEmail.email

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

相关推荐
Devil枫16 分钟前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
小袁在上班6 小时前
Python 单元测试中的 Mocking 与 Stubbing:提高测试效率的关键技术
python·单元测试·log4j
测试19987 小时前
外包干了2年,快要废了。。。
自动化测试·软件测试·python·面试·职场和发展·单元测试·压力测试
安冬的码畜日常10 小时前
【The Art of Unit Testing 3_自学笔记06】3.4 + 3.5 单元测试核心技能之:函数式注入与模块化注入的解决方案简介
笔记·学习·单元测试·jest
王解11 小时前
Jest项目实战(2): 项目开发与测试
前端·javascript·react.js·arcgis·typescript·单元测试
程序员小雷1 天前
软件测试基础:单元测试与集成测试
python·功能测试·selenium·测试工具·单元测试·集成测试·压力测试
王解1 天前
Jest进阶知识:深入测试 React Hooks-确保自定义逻辑的可靠性
前端·javascript·react.js·typescript·单元测试·前端框架
程序员雷叔1 天前
外包功能测试就干了4周,技术退步太明显了。。。。。
功能测试·测试工具·面试·职场和发展·单元测试·测试用例·postman
安冬的码畜日常1 天前
【玩转 Postman 接口测试与开发2_005】第六章:Postman 测试脚本的创建(上)
javascript·测试工具·单元测试·postman·bdd·chai
程序员小雷1 天前
应对自动化测试中的异步操作:策略与实践
功能测试·selenium·测试工具·jmeter·单元测试·测试用例·postman