目录
Python接口测试课程(第一天)-Python基础
Python接口测试课程(第二天)-接口测试快速实践
Python接口测试课程(第三天)-接口安全验证,参数化及断言
Python接口测试课程(第四天)-接口测试框架实现
更多学习资料请加添加作者微信:lockingfree获取
第四天: Python接口测试框架
什么是框架
目前主流接口测试方案
- 工具派
- Java派
- Python派
接口平台
框架类型
- 录制回放
- 数据驱动
行为驱动
框架的分层与规划
框架分层
- 表示层: (用户界面)
- 业务逻辑层: (读取数据,配置并组装发送请求)+执行控制层(pytest)
数据层: (配置读取/数据读取/数据库连接/其他(log/email)
框架规划
- case: 测试用例目录
- user: (用户模块)
- test_user.py: 测试用例
- case.py: 用例公共方法
- user: (用户模块)
- data: 数据文件目录
- test_user_data.xlsx: 测试用例数据文件
- conf: 配置文件目录
- default.conf: 默认配置文件
- report: pytest生成的报告保存路径
- log: log保存路径,按天生成log
- common: 公共方法目录
- config.py: 配置文件读取
- data.py: 数据文件读取
- db.py: 数据库连接
- log.py: 日志配置
send_email.py: 发送邮件配置
框架实现
conf/default.conf: 表示层-项目配置文件
```
[runtime]
log_level=debug
report_dir=report
log_dir=log
timeout=10
[server]
test = http://127.0.0.1:5000
stage = http://127.0.0.1:6000
prod = http://127.0.0.1:7000
[db_test]
host = localhost
port = 3307
db = api
user = root
passwd =
[db_stage]
[db_prod]
[email]
server = smtp.sina.com
user = test_results@sina.com
pwd = ******
subject = Api Test Ressult
receiver = superhin@126.com
#### data/test_user_data.xlsx: 表示层-用例数据文件 - reg表(sheet名为reg) TestCase | Url | Method | DataType | Data | Code | Msg ---|---|---|---|---|---|--- test_reg_normal | /api/user/reg/ | POST | JSON | {"name": "{NAME}", "passwd": "123456"} | 100000 | 成功 | - login表(sheet名为login) TestCase | Url | Method | DataType | Data | ResponseText ---|---|---|---|---|--- test_login_normal | /api/user/login/ | POST | FORM | {"name": "张三", "passwd": "123456"} | 登录成功 | - SQL表(sheet名为SQL) checkUser | select * from user where name={NAME} |---|--- checkUserPasswd | select * from user where name={NAME} and passwd={PASSWD} #### common/config.py:数据层-config文件读取
"""
- 从配置文件中获取各个段信息
- 返回一个项目的绝对路径
"""
import os
import configparser
相对导入包的问题
pro_path = os.path.dirname(os.path.dirname(os.path.abspath(file)))
class Config(object):
def init(self, filename="default.conf"):
self.cf = configparser.ConfigParser()
self.cf.read(os.path.join(pro_path,"conf",filename))
def get_runtime(self, option): return self.cf.get("runtime", option) def get_server(self, option): return self.cf.get("server", option) def get_db_test(self, option): return self.cf.get("db_test", option) def get_email(self, option): return self.cf.get("email",option)
if name == "main":
c = Config()
print(c.get_runtime("log_level"))
print(c.get_server("test"))
#### data.py: 数据层-数据文件读取
"""
- 从Excel中读取接口的数据
- 读取Sql命令
"""
import xlrd
import sys
import os
sys.path.append("..")
from common.config import pro_path
class Data(object):
def init(self, filename):
data_file_path = os.path.join(pro_path,"data",filename)
self.wb = xlrd.open_workbook("../data/test_user_data.xlsx")
def get_case(self,sheet_name, case_name): sh = self.wb.sheet_by_name(sheet_name) for i in range(1, sh.nrows): if sh.cell(i,0).value == case_name: return sh.row_values(i) print("用例名未找到") return None def get_sql(self, sql_name): sh = self.wb.sheet_by_name("SQL") for i in range(sh.nrows): if sh.cell(i,0).value == sql_name: return sh.cell(i,1).value print("sql未找到") return None
if name == "main":
d = Data("test_user_data.xlsx")
print(d.get_case("reg","test_reg_normal"))
print(d.get_sql("checkUser"))
#### db.py: 数据层-数据库连接
"""
- 从配置文件中读取数据库配置
- 连接数据库
- 执行sql并返回所有结果
"""
import sys
import pymysql
sys.path.append("..")
from common.config import Config
class DB(object):
def init(self):
c = Config()
self.conn = pymysql.connect(host=c.get_db_test("host"),
port=int(c.get_db_test("port")),
db=c.get_db_test("db"),
user=c.get_db_test("user"),
passwd=c.get_db_test("passwd"),
charset="utf8")
self.cur = self.conn.cursor() def do_sql(self, sql): self.cur.execute(sql) return self.cur.fetchall() def __del__(self): self.cur.close() self.conn.close()
if name == "main":
db = DB()
print(db.do_sql("select * from user"))
#### log.py: 数据层-log配置
"""
- 配置log输出格式 time - loglevel - file - func - line - msg
- 支持输出到log文件及屏幕
- 支持返回一个logger,让其他模块调用
"""
import sys
sys.path.append("..")
from common.config import Config, pro_path
import time
import logging
import os
class Log():
@classmethod
def config_log(cls):
cf = Config()
log_dir = os.path.join(pro_path, cf.get_runtime("log_dir"))
today = time.strftime("%Y%m%d", time.localtime(time.time()))
log_file = os.path.join(log_dir, today+".log")
# 获取一个标准的logger, 配置loglevel cls.logger = logging.getLogger() cls.logger.setLevel(eval("logging." + cf.get_runtime("log_level").upper())) # 建立不同handler fh = logging.FileHandler(log_file, mode="a",encoding=‘utf-8’) ch = logging.StreamHandler() # 定义输出格式 ft = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s") fh.setFormatter(ft) ch.setFormatter(ft) # 把定制handler 添加到我们logger cls.logger.addHandler(fh) cls.logger.addHandler(ch) @classmethod def get_logger(cls): cls.config_log() return cls.logger
if name == "main":
l= Log.get_logger()
l.info("abc")
l.debug("hello, debug")
#### send_email.py: 数据层-邮件服务器连接
"""
- 从配置文件中读取stmp配置
- 从report文件夹下打开report.html,发送邮件
"""
import smtplib
from email.mime.text import MIMEText
import os
import sys
sys.path.append("..")
from common.config import Config, pro_path
from common.log import Log
def send_email(report_name):
cf = Config()
logger = Log.get_logger()
report_file = os.path.join(pro_path, cf.get_runtime("report_dir"),report_name)
with open(report_file, "rb") as f: body = f.read() # 格式化email正文 msg = MIMEText(body, "html", "utf-8") # 配置email头 msg["Subject"] = cf.get_email("subject") msg["From"] = cf.get_email("user") msg["To"] = cf.get_email("receiver") # 连接smtp服务器,发送邮件 smtp = smtplib.SMTP() smtp.connect(cf.get_email("server")) smtp.login(cf.get_email("user"),cf.get_email("pwd")) smtp.sendmail(cf.get_email("user"), cf.get_email("receiver"), msg.as_string()) print("邮件发送成功")
if name == "main":
send_email("report.html")
#### case/case.py: 业务逻辑层, 为用例执行封装方法
"""
- 加载数据
- 发送接口
- 为用例封装一些方法
"""
import sys
sys.path.append("..")
from common.log import Log
from common.config import Config
from common.db import DB
from common.data import Data
import json
import requests
class Case(object):
def init(self):
self.logger = Log.get_logger()
self.cf = Config()
def load_data(self, data_file): self.data = Data(data_file) def set_env(self, env): self.env = env def run_case(self, sheet_name, case_name, var={}): case_data = self.data.get_case(sheet_name, case_name) url = self.cf.get_server(self.env) + case_data[1] data = case_data[4].format(**var) if case_data[3].lower() == "form": data = json.loads(data) headers = {} else: headers = {"content-type": "application/json"} if case_data[2].lower() == "get": resp = requests.get(url=url) else: resp = requests.post(url=url, headers=headers, data=data) return resp.text def check_response(self): pass def check_db(self, sql_name, vars={}): sql = self.data.get_sql(sql_name).format(**vars) return self.db.exec_sql(sql)
if name == "main":
c = Case()
c.set_env("test")
c.load_data("test_user_data.xlsx")
r = c.run_case("login", "test_login_normal")
print(r)
#### case/user/test_user.py: 表示层: 测试用例脚本
import sys
import random
import pytest
sys.path.append("../..")
from case.case import Case
case = Case()
def setup_module(module):
case.set_env('dev')
case.load_data('test_user_data.xlsx')
def test_login_normal():
result case.run("login", "test_login_normal")
if name == 'main':
pytest.main(["-q", "test_user.py"])
#### run_all.py: 表示层: 执行所有用例入口
import os
import time
from util.config import Config
from util.e_mail import send_email
import pytest
def main():
cf = Config()
report_dir = cf.get_report_dir()
now = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
report_name = os.path.join(report_dir, 'report_' + now + '.html')
pytest.main(["-q", "case", "--html=" + report_name])
send_email(report_name)
if name == 'main':
main()
```