采用Excel
为数据基础编写一个接口自动化测试框架。
这张图是我的excel接口测试框架的一些设计思路。
目录/文件 | 说明 | 是否为python包 |
---|---|---|
common | 公共类 | 是 |
core | 核心类,封装requests 等 |
是 |
data | 测试使用的excel文件存放目录 | |
logs | 日志目录 | |
tests | 测试用例目录 | 是 |
utils | 工具类,如:日志 | 是 |
config.py | 配置文件 | |
run.py | 执行文件 |
本次依然采用的是智学网
登录接口。
config.py
#!/usr/bin/env python3
# coding=utf-8
import os
class CF:
"""配置文件"""
# 项目目录
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
# Excel配置
NUMBER = 0
NAME = 1
METHOD = 2
URL = 3
ROUTE = 4
HEADERS = 5
PARAMETER = 6 # 参数
EXPECTED_CODE = 7 # 预期响应码
EXPECTED_REGULAR = 8 # 预期正则
EXPECTED_VALUE = 9 # 预期结果值
SPEND_TIME = 10 # 响应时间
TEST_RESULTS = 11 # 测试结果
EXTRACT_VARIABLE = 12 # 提取变量
# 邮箱配置
EMAIL_INFO = {
‘username‘: ‘1084502012@qq.com‘,
‘password‘: ***,
‘smtp_host‘: ‘smtp.qq.com‘,
‘smtp_port‘: 465
}
# 收件人
ADDRESSEE = [‘1084502012@qq.com‘]
if __name__ == ‘__main__‘:
print(CF.EXPECTED_CODE)
这里要说明一下为什么Excel配置
要从0开始:因为测试用例读取出来之后是个列表,为了方便这里采用0开始。
excelset.py
#!/usr/bin/env python
# coding=utf-8
import os
import openpyxl
from config import CF
from openpyxl.styles import Font
from openpyxl.styles import PatternFill
class ExcelSet:
"""Excel配置"""
def __init__(self):
self.path = os.path.join(CF.BASE_DIR, ‘data‘, ‘usercase.xlsx‘)
self.wb = openpyxl.load_workbook(self.path)
self.table = self.wb.active
def get_cases(self, min_row=2):
"""获取用例"""
all_cases = []
for row in self.table.iter_rows(min_row=min_row):
all_cases.append((self.table.cell(min_row, CF.NAME + 1).value,
min_row, [cell.value for cell in row]))
min_row += 1
return all_cases
def write_results(self, row_n, col_n, value, color=True):
"""写入结果"""
cell = self.table.cell(row_n, col_n + 1)
cell.value = value
font = Font(name=‘微软雅黑‘, size=16)
cell.font = font
if color:
if value.lower() in ("fail", ‘failed‘):
fill = PatternFill("solid", fgColor="FF0000")
cell.fill = fill
elif value.lower() in ("pass", "ok"):
fill = PatternFill("solid", fgColor="00CD00")
cell.fill = fill
self.wb.save(self.path)
if __name__ == ‘__main__‘:
read = ExcelSet()
print(read.get_cases())
logger.py
#!/usr/bin/env python3
# coding=utf-8
import os
import logging
from config import CF
from datetime import datetime
class Logger:
def __init__(self):
self.logger = logging.getLogger()
if not self.logger.handlers:
self.logger.setLevel(logging.DEBUG)
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler(self.log_path, encoding=‘utf-8‘)
fh.setLevel(logging.DEBUG)
# 创建一个handler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# 定义handler的输出格式
formatter = logging.Formatter(self.fmt)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 给logger添加handler
self.logger.addHandler(fh)
self.logger.addHandler(ch)
@property
def log_path(self):
logs_path = os.path.join(CF.BASE_DIR, ‘logs‘)
if not os.path.exists(logs_path):
os.makedirs(logs_path)
now_month = datetime.now().strftime("%Y%m")
return os.path.join(logs_path, ‘{}.log‘.format(now_month))
@property
def fmt(self):
return ‘%(levelname)s %(asctime)s %(filename)s:%(lineno)d %(message)s‘
log = Logger().logger
if __name__ == ‘__main__‘:
log.info("你好")
regular.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import re
from utils.logger import log
from common.variables import is_vars
from core.serialize import is_json_str
class Regular:
"""正则类"""
def __init__(self):
self.reg = re
def finds(self, string):
return self.reg.findall(r‘\{{(.*?)}\}‘, string)
def subs(self, keys, string):
result = None
log.info("提取变量:{}".format(keys))
for i in keys:
if is_vars.has(i):
log.info("替换变量:{}".format(i))
result = self.reg.sub(r"\{{%s}}" % i, is_vars.get(i), string)
log.info("替换结果:{}".format(result))
return result
def find_res(self, exp, string):
"""在结果中查找"""
if is_json_str(string):
return self.reg.findall(r‘\"%s":"(.*?)"‘ % exp, string)[0]
else:
return self.reg.findall(r‘%s‘ % exp, string)[0]
variables.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
class VariablePool:
"""全局变量池"""
def get(self, name):
"""获取变量"""
return getattr(self, name)
def set(self, name, value):
"""设置变量"""
setattr(self, name, value)
def has(self, name):
return hasattr(self, name)
is_vars = VariablePool()
request.py
#!/usr/bin/env python
# coding=utf-8
import urllib3
import requests
from config import CF
from utils.logger import log
from common.regular import Regular
from common.setResult import replace_param
from core.serialize import deserialization
from requests.exceptions import RequestException
from common.variables import is_vars
urllib3.disable_warnings()
class HttpRequest:
"""二次封装requests方法"""
def __init__(self):
self.r = requests.session()
self.reg = Regular()
def send_request(self, case, **kwargs):
"""发送请求
:param case: 测试用例
:param kwargs: 其他参数
:return: request响应
"""
if case[CF.URL]:
is_vars.set(‘url‘, case[CF.URL])
if case[CF.HEADERS]:
is_vars.set(‘headers‘, deserialization(case[CF.HEADERS]))
method = case[CF.METHOD].upper()
url = is_vars.get(‘url‘) + case[CF.ROUTE]
self.r.headers = is_vars.get(‘headers‘)
params = replace_param(case)
if params: kwargs = params
try:
log.info("Request Url: {}".format(url))
log.info("Request Method: {}".format(method))
log.info("Request Data: {}".format(kwargs))
if method == "GET":
response = self.r.get(url, **kwargs)
elif method == "POST":
response = self.r.post(url, **kwargs)
elif method == "PUT":
response = self.r.put(url, **kwargs)
elif method == "DELETE":
response = self.r.delete(url, **kwargs)
elif method in ("OPTIONS", "HEAD", "PATCH"):
response = self.r.request(method, url, **kwargs)
else:
raise AttributeError("send request method is ERROR!")
log.info(response)
log.info("Response Data: {}".format(response.text))
return response
except RequestException as e:
log.exception(format(e))
except Exception as e:
raise e
if __name__ == ‘__main__‘:
log.info("你好")
test_api.py
#!/usr/bin/env python
# coding=utf-8
import unittest
from parameterized import parameterized
from common.excelset import ExcelSet
from core.request import HttpRequest
from common.checkResult import check_result
from common.setResult import get_var_result
excel_set = ExcelSet()
class TestApi(unittest.TestCase):
"""测试接口"""
@classmethod
def setUpClass(cls) -> None:
cls.req = HttpRequest()
@classmethod
def tearDownClass(cls) -> None:
cls.req.r.close()
@parameterized.expand(excel_set.get_cases())
def test_api(self, name, number, case):
"""
测试excel接口用例
"""
r = self.req.send_request(case)
get_var_result(r, number, case)
check_result(r, number, case)
if __name__ == ‘__main__‘:
unittest.main(verbosity=2)
run.py
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import os
import unittest
from config import CF
from utils.HTMLTestRunner import HTMLTestRunner
test_case = unittest.defaultTestLoader.discover(‘tests‘, ‘test*.py‘)
def report_path():
reports_path = os.path.join(CF.BASE_DIR, ‘report‘)
if not os.path.exists(reports_path):
os.makedirs(reports_path)
return os.path.join(reports_path, ‘index.html‘)
if __name__ == ‘__main__‘:
with open(report_path(), ‘wb‘) as fp:
runner = HTMLTestRunner(stream=fp,
title=‘Excel接口测试‘,
description="用例执行情况",
verbosity=2)
runner.run(test_case)
测试报告HTMLrunner.py文件来自网络,需要可联系我
发送邮件我还没有调试好,等我调试好了在更新发送邮件吧。
unitest+excel+requests接口自动化测试框架
原文:https://www.cnblogs.com/wxhou/p/13394677.html