接口自动化实战之项目搭建

新建项目及python包

创建一个新的python项目,在下面建一个run.py文件作为unittest的TestRunner来跑测试用例;再新建一个tests包放测试用例:
在这里插入图片描述
新建config包来存放配置文件:
在这里插入图片描述
新建libs包存放第三方模块:
在这里插入图片描述
新建reports包存放执行测试用例后生成的测试报告:
在这里插入图片描述
新建data包存放调用接口和断言时所需要准备的测试数据:
在这里插入图片描述
新建common包存放公共模块:
在这里插入图片描述
新建logs包存放日志:
在这里插入图片描述

跑通用例

接着在tests包下新建一个test_demo.py,先不写任何实质上的内容,仅仅是个为了测试的空壳。

  • test_demo.py
import unittest

class Test_demo(unittest.TestCase):
    #先什么都不写,pass当做这个用例100%能执行通过!
    def test_demo(self):
        pass

接着我们就开始写run.py

  • run.py
import unittest
import os
from datetime import datetime
from config import config
from libs.HTMLTestRunnerNew import HTMLTestRunner
#加载用例
loader = unittest.TestLoader()
#自动检索所有测试用例
"""
问题1:路径的重用
这里我们准备传入测试用例所在路径:tests,直接在这里用变量定义出来自然是最简单的,
但后面可能还需要更多需要传入路径的场景,所以我们需要找个地方配置所有的路径。而一
想到配置,我们就会想到放到config目录下。但具体我们应该用什么配置呢?yaml吗?但
yaml只适合配置静态的配置,路径是会随着项目的转移而发生变化,所以我们可以用一个py
文件来配置所有的路径信息。在config.py定义了测试用例的路径后,我们可以直接import
过来。
@路径配置详见config.py
"""
cases = loader.discover(config.CASES_PATH)

"""
问题2:报告时间戳格式的生成
这里我们需要定义报告的名称。而每次生成的报告如果以同一文件名(如:report.html)命名
的话,就会覆盖之前的报告内容。当我们需要找回历史报告(如上个月的报告)时就找不到了,所以
我们需要用时间戳格式命名报告的文件名,这样不仅执行时间可以一目了然,还能找到对应时间的
报告回顾。
"""
#定义时间戳
time_format = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
#测试报告文件名称
report_name = "report-{}.html".format(time_format)
#测试报告路径
report_path = os.path.join(config.REPORT_PATH,report_name)
#执行测试用例
with open(report_path,mode='wb') as f:
    #这里我们用第三方模块:HTMLTestRunnerNew来生成测试报告
    runner = HTMLTestRunner(
        f,
        title = "接口自动化测试报告",
        description = "测试报告",
        tester = "tom"
    )
    #执行测试用例
    runner.run(cases)

问题1中我们需要把项目中的所有包的路径都存放到py文件中,我们需要在包里面新建一个config.py:
在这里插入图片描述

  • config.py
import os

#配置文件路径
CONFIG_PATH = os.path.dirname(os.path.abspath(__file__))

#项目路径
ROOT_PATH = os.path.dirname(CONFIG_PATH)

#测试用例路径
CASES_PATH = os.path.join(ROOT_PATH,"tests")

#测试报告路径
REPORT_PATH = os.path.join(ROOT_PATH,"reports")

在libs包下存放刚刚用到的第三方模块HTMLTestRunnerNew.py:
在这里插入图片描述
各个包、TestCase、TestRunner都准备好后,接下来我们去到run.py执行测tests下的所有测试用例,执行结果如下,表明unittest中的TestCase和TestRunner是能跑通的:
在这里插入图片描述
而如果运行时报了下面的错,则表明测试用例是有问题的,要么是缺少__init__.py文件,要么是测试用例命名不规范(注意,命名最好以test开头),要么是测试用例内部的代码出了问题:
在这里插入图片描述

通用模块的构建

我们需要把之前讲到的各个测试框架的组件加到common中,包括:excel_handler、logging_handler、yaml_handler、request_handler、sql_handler:
在这里插入图片描述

excel_handler

import openpyxl
import pprint

class excel_handler():
    """初始化实例参数"""
    def __init__(self,filepath):
        self.filepath = filepath
        self.workbook = None

    """定义open_file方法,返回workbook对象"""
    def open_file(self):
        workbook = openpyxl.load_workbook(self.filepath)
        self.workbook = workbook
        return workbook

    """通过workbook对象获取sheet对象"""
    def get_sheet(self,name):
        workbook = self.open_file()
        sheet = workbook[name]
        return sheet

    """获取表单里的每一个单元格对象的value"""
    def get_data(self,name):
        #获取表单对象
        sheet = self.get_sheet(name)
        #获取表单对象中的所有行
        rows = list(sheet.rows)
        #定义一个列表,用来接收每一行的值
        data = []
        #定义一个列表,获取所有的标题
        tittles = []
        #遍历第一行rows[0],获取所有行的value属性,放入tittles中
        for tittle in rows[0]:
            tittles.append(tittle.value)
        #遍历第二行后的所有数据(排除掉第一行是因为已经遍历完了第一行的标题)
        for row in rows[1:]:
            #定义一个字典,存放每一行的值,每一个值是一个键值对,key是标题,值是该行对应的value属性。
            row_data = {}
            #遍历拿到的每一行,获取每一行的每一个单元格放入row_data中.其中key为标题,value为该单元格对应的值.
            for index,cell in enumerate(row):
                row_data[tittles[index]] = cell.value
            #把每一行的数据放入data中
            data.append(row_data)
        return data

    """对单元格的value属性赋值,写入到excel中"""
    def write(self, sheet_name, row, column, data):
        sheet = self.get_sheet(sheet_name)
        sheet.cell(row, column).value = data
        self.save()
        self.close()

    """对excel做修改后,保存excel文件"""
    def save(self):
        self.workbook.save(self.file_path)

    """关闭workbook对象,释放内存"""
    def close(self):
        self.workbook.close()

logging_handler

import logging

def log_handler(
        logger_name = "logger",
        logger_level = "DEBUG",
        stream_level = "DEBUG",
        file_name = "None",
        file_level = "DEBUG",
        fmt = '%(asctime)s--%(filename)s--No:%(lineno)d--%(levelname)s:%(message)s',

):

    #初始化日志收集器
    logger = logging.getLogger(logger_name)
    #设置日志收集器的日志级别
    logger.setLevel(logger_level)
    #初始化流处理器
    stream_handler = logging.StreamHandler()
    #设置流处理器的日志级别
    stream_handler.setLevel(stream_level)
    #将流处理器与日志收集器绑定起来
    logger.addHandler(stream_handler)
    #设置格式
    fmt = logging.Formatter(fmt)
    #设置流处理器的日志输出格式
    stream_handler.setFormatter(fmt)
    #如果传入的file_name不为空,则初始化文件处理器
    if file_name:
        #初始化文件处理器
        file_handler = logging.FileHandler(file_name,encoding='UTF-8')
        #设置文件处理器的日志级别
        file_handler.setLevel(file_level)
        #将文件处理器与日志收集器绑定起来
        logger.addHandler(file_handler)
        #设置文件处理器的日志输出格式
        file_handler.setFormatter(fmt)
    return logger

yaml_handler

import yaml

def read_yaml(file):
    """读取 yaml 文件"""
    with open(file, encoding='utf8') as f:
        conf = yaml.load(f, Loader=yaml.SafeLoader)
    return conf


def write_yaml(file, data):
    with open(file, 'w', encoding='utf8') as f:
        yaml.dump(data, f)

request_handler

import requests
import logging

def requests_handler(
#准备请求接口时所需要的参数:请求地址、请求方法、请求头、url参数、form参数、json参数
        url,
        method = "get",
        headers = None,
        params = None,
        data = None,
        json = None,
):
    """由于requests里面封装的各种请求方法(get、post、put、delete、head等)最后都要调用request方法,所以
    可以直接调用requests完成所有请求方法的调用,而不用再一个个请求方法封装"""
    req = requests.request(method,
                           url,
                           data = data,
                           json = json,
                           params = params,
                           headers = headers
                        )
    try:
        #由于接口最后返回的都是json格式的数据,所以这里调用json方法返回json格式数据
        return req.json()
    except Exception as e:
        #如果调用json方法报错,则说明返回格式不是json格式,这时应该抛异常
        logging.error("返回的格式不是json格式{}".format(e))
        return None

sql_handler

import pymysql
from pymysql.cursors import DictCursor

class mysql_handler():
    #初始化,准备需要建立数据库连接的所有参数
    def __init__(
            self,
            host = None,
            port = 3306,
            user = None,
            password = None,
            charset="utf8",
            cursorclass=DictCursor
    ):
       #建立数据库连接,传入准备好的参数
       self.con = pymysql.connect(
           host=host,
           port=port,
           user=user,
           password=password,
           charset=charset,
           cursorclass=cursorclass
       )
       self.cursor = self.con.cursor()
    #查询方法,传入需要执行查询的sql,以及控制是选择fetchall还是fetchone的参数is_one
    def query(self,sql,is_one=True):
        #调用游标对象的execute方法执行查询的sql
        self.cursor.execute(sql)
        #如果is_one为true,则只返回结果中的第一条记录
        if is_one:
            return self.cursor.fetchone()
        #如果is_one为false,则返回结果中的所有记录
        return self.cursor.fetchall()
    #关闭游标对象及连接对象
    def close(self):
        self.cursor.close()
        self.con.close()
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值