目录
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()
```