使用类、模块、包封装代码--架构及封装代码

模块简介

(1)一个模块(就是py文件)存储解决一个大问题的相关代码(多个函数、多个类等)
(2)模块是员工分工编程的基本单位

针对面向模块测试框架

1)面向过程的编程
    堆代码
    函数
2)面向对象的编程
  a)创建类
    配置文件相关类、数据库相关的类
    具体过程:
        编写构造方法 定义成员变量
        编写普通成员方法 处理成员变量的数据
  b)创建对象
  c)调用方法
3)模块 是一个文件
真正编程时,会把函数、类都放入模块中,他人在使用函数和类的时候只需要导入模块就可以

使用包/目录封装代码

包简介

(1)包就是目录,文件夹
(2)一个文件夹中存放多个同类的文件,或者存放说解决同一问题的多个文件

规划

common
    casedata.py -存放测试用例数据程序
    db.py   -存放数据库相关程序
        class DB
            def  __init__(self)
            def  conn_db(self) -- 连接数据库方法
            def  get_sqlfiles(self,sqlfiles=[]) -- 获取sql文件方法
            def  read_sqls(self,sqlfiles=[]) -- 读取sql语句方法
            def  init_db(self,sqlfile=[]) -- 数据初始化
            def  check_db(self,sql,param,db_expect_rows) -- 落库检查
    entry.py -入口程序
        class Entry -- 入口类
            def  __init__(self)
            def  get_which_server(self) -- 获得接口服务器信息
            def  get_which_db(self) -- 获取数据库服务器信息
    log.py -日志程序
        def log() -- 生成日志方法
    postdata.py -- 发送数据程序
    serverconf.py -- 服务器相关程序
        class ServerConf
            def  __init__(self)
            def  get_host(self) -- 获得接口地址方法
conf -- 存放配置文件的目录
  entry.ini -- 服务器节点名
  db.conf -- 数据库服务器配置信息
  server.conf -- 接口服务器配置信息
excelcase -- 存放用例文件的目录
  login.xlsx -- 存放登录用例
  signup.xlsx -- 存放注册用例
initsqls -- 存放sql语句目录
  login.sql -- 存放登录SQL语句
  signup.sql -- 存放注册SQL语句
log -- 存放测试日志的目录
runtest -- 存放运行代码的目录
    run.py  --运行测试代码
tests --存放测试代码的目录
    login.py -- 存放登录测试代码
    signup.py -- 存放注册测试代码
a)创建多个文件夹,内部分门别类的存放不同的文件
b)可以使用函数编程,多个函数最好放在不同的文件中,py文件就叫做模块,具体讲,文件名是模块名
c)实际编程过程中,自己与同事会有分工,一个人负责编写一个或多个py文件,一个文件中会编写若干个函数或类(类中又有最多一个构造方法、多个成员方法),一般,一个文件中的程序都是有关联的,比如db.py中只存放与数据库操作有关系的函数或者类

实现模块化测试框架代码

log.py

import logging,time
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

# 调试
if __name__=='__main__':# 在这个文件(log.py)中执行时,下面的代码才执行
    log().info('成功时的日志')
    log().warning('测试失败时的日志')
    log().error('出错时的日志')

entry.py

import configparser
from project_M1.common import log


class Entry:
    def __init__(self):
        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.log().error('读取接口配置文件entry.ini失败' + e)

    def get_which_server(self):
        # 根据目录不同log.log()表示log文件中的函数
        log.log().info('本次测试的接口服务器是:' + self.which_server)
        return self.which_server

    def get_which_db(self):
        log.log().info('本次测试的数据库服务器是:' + self.which_db)
        return self.which_db


if __name__ == '__main__':
    server = Entry().get_which_server()
    db = Entry().get_which_db()
    print('接口服务器是:' + server + ",数据库服务器是:" + db)

serverconf.py

from project_M1.common import entry, log
import configparser  # python自带的模块 不需要from
class ServerConf:  # 获取接口服务器的地址
    def __init__(self):
        try:
            # 获得要测试的接口服务器的名字
            which_server = entry.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.log().error('接口服务器地址[server.conf]获取失败' + e)

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


if __name__ == '__main__':
    print(ServerConf().get_host())

db.py

from project_M1.common import entry, log
import configparser, os, pymysql

class DB:
    def __init__(self):
        """
        读取db.conf文件,获得数据库服务器的信息
        返回内容是 数据库服务器信息
        """
        try:  # 异常处理
            # 获得测试要用的数据库服务器的节点名
            which_db = entry.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.log().info('数据库信息==' + str(self.dbinfo))
        except Exception as e:
            log.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.log().info('连接数据库成功')
            return conn
        except Exception as e:
            log.log().error('数据库连接出错' + e)

    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.log().info('获得sql文件名' + str(sqlfiles) + '列表成功')
            return sqlfiles
        except Exception as e:
            log.log().error('获得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.log().info('读取初始化sql语句成功==' + str(sqlfiles))
            return sqls
        except Exception as e:
            log.log().error('读取初始化sql语句出错' + e)

    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.log().info('初始化数据库成功')
        except Exception as e:
            log.log().error('初始化数据库出错')

    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.log().info(case_name + '==落库检查==通过')
            else:
                log.log().warning(case_name + '==落库检查==失败==预期行数:' + str(expect_db_rows) + ',实际行数:' + str(dbactual))
        except Exception as e:
            log.log().error('落库检查出错' + e)

if __name__=='__main__':
    DB().init_db()
    DB().check_db('测试','select count(*) from user',{'name':123},3)

casedata.py

import pandas
from project_M1.common import log
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.log().info('读取用例文件' + excel + '成功')
        return data
    except Exception as e:
        log.log().error('读取测试用例文件出错')

if __name__ =='__main__':
    print(read_cases('login.xlsx'))

postdata.py

import requests
from project_M1.common import log
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.log().info('比对登录接口返回==' + case_name + '==通过')
            else:
                log.log().warning('比对登录接口返回==' + case_name + '==失败==预期结果:' + expect + ',实际结果:' + actual)
        elif 'application/json' in res.headers['Content-Type']:
            actual = res.json()  # 实际结果(字典)
            # 比对接口返回结果
            if expect == actual:
                log.log().info("比对注册接口返回==" + case_name + '==通过')
            else:
                log.log().warning('比对注册接口返回==' + case_name + '==失败==预期结果:' + str(expect) + ',实际结果:' + str(actual))
        else:  # xml
            pass  # 暂时不写代码
    except Exception as e:
        log.log().error('发送数据,比对响应结果出错')
if __name__=='__main__':
    post('http://192.168.139.137/exam/login/',{'username':'admin','password':'123456'},'成功登录测试','登录成功')

login.py

from project_M1.common import db,log,serverconf,casedata,postdata
def test_login():
    """
    测试登录接口
    :return:
    """
    # 数据库初始化
    db.DB().init_db(['login.sql'])
    # 读测试用例数据
    cases = casedata.read_cases('login.xlsx')  # 用例:用例名0、参数数据1、预期结果2
    # 读接口服务器地址
    host = serverconf.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]  # 预期接口的返回结果
            postdata.post(address, argument, case_name, expect)
    except Exception as e:
        log.log().error('登录接口测试出错' + e)
if __name__=='__main__':
    test_login()

signup.py

from project_M1.common import db,log,serverconf,casedata,postdata
def test_signup():
    """
    测试注册接口
    :return:
    """
    # 数据库初始化
    db.DB().init_db(['signup.sql'])
    # 读测试用例
    cases = casedata.read_cases('signup.xlsx')
    # 接口地址
    host = serverconf.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]  # 预期数据库行数
            # 发送请求
            postdata.post(address, argument, case_name, expect)
            # 落库检查
            db.DB().check_db(case_name,sql,argument,dbexpect)
    except Exception as e:
        log.log().error('注册接口测试出错' + e)

if __name__=='__main__':
    test_signup()


posted @ 2021-11-07 17:10  暄总-tester  阅读(252)  评论(0)    收藏  举报