目录
一、框架搭建流程
1. 封装接口请求
封装post和get方法方便在使用requests模块发送请求时,仅调用一个方法即可
-
#!/usr/bin/env python
-
# -*- coding:utf-8 -*-
-
# fileName: run_method.py
-
import requests
-
-
-
class
RunMethod(
object):
-
-
def
post_main(
self, url, headers, data):
-
# 忽略不安全的请求警告信息
-
requests.packages.urllib3.disable_warnings()
-
# 遇到requests的ssl验证,若想直接跳过不验证,设置verify=False即可
-
response = requests.post(url=url, headers=headers, data=data, verify=
False)
-
return response
-
-
def
get_main(
self, url, headers, data=None):
-
# 忽略不安全的请求警告信息
-
requests.packages.urllib3.disable_warnings()
-
response = requests.get(url=url, headers=headers, data=data, verify=
False)
-
return response
-
-
def
run_main(
self, method, url, headers, data=None):
-
# 忽略不安全的请求警告信息
-
requests.packages.urllib3.disable_warnings()
-
requests.adapters.DEFAULT_RETRIES =
5
-
-
if method ==
"Post":
-
res = self.post_main(url, headers, data)
-
elif method ==
"Get":
-
res = self.get_main(url, headers, data)
-
# 将响应的的数据以字典数据结构和json数据格式返回
-
return res.json()
2. 存储接口信息
使用yml文件作为配置文件,存储每一个接口信息,主要包括”接口名称、method、url、data和headers“,以及预期结果”expected“
-
# fileName: api_config.yml
-
-
host: https://XXX.com
-
-
login:
-
name: 【登录】手机号码登录
-
method: Post
-
url: /v1/login/
-
data:
-
phone_country: 86
-
phone_number: 1210000000
-
password: 123456
-
device_id: XXX
-
device_name: Iphone8plus
-
device_pubkey: XXX
-
headers:
-
Content-Type: application/x-www-form-urlencoded
-
Authorization: Basic XXX
-
-
asset_main:
-
name: 【资产】用户资产列表页
-
method: Get
-
url: /v2/main/
-
headers:
-
Authorization: Bearer
-
app-version: 4.8.0
-
device-id: XXX
-
expected:
-
success:
true
-
-
lend:
-
name: 发送请求
-
method: Post
-
url: /v1/lend/
-
data:
-
payment_password: 123456
-
amount: 100
-
pledge_source: 1
-
headers:
-
Content-Type: application/x-www-form-urlencoded
-
Authorization: Bearer
-
device-id: XXX
-
app-version: 4.8.0
3. 封装登录和其他方法
1)登录方法用于获取并存储token,存储至access_token.yml中;
2)参数”api_name“的取值是配置文件api_config.yml中的接口名称,比如:login、asset_main和loan_lend。同样封装了一个类ReadYaml,方法是取配置文件中各接口的”method、url、data、headers和expected“等;
3)除了登录接口,其他接口的headers均需要token值,所以在处理headers时要区分登录接口和其他接口;
4)建议:添加异常处理捕获异常,输出自定义的错误信息。
-
#!/usr/bin/env python
-
# -*- coding:utf-8 -*-
-
# fileName: app_api.py
-
from common.run_method
import RunMethod
-
from common.read_info
import ReadYaml
-
from common.get_log
import get_log
-
from common.deal_token
import write_token
-
import json
-
-
-
class
AllApi(
object):
-
def
__init__(
self):
-
self.run = RunMethod()
-
self.read = ReadYaml()
-
self.logger = get_log()
-
-
# 登录,获取token
-
def
login(
self, api_name):
-
try:
-
# 获取接口请求参数
-
method = self.read.get_method(api_name)
-
url = self.read.get_url(api_name)
-
data = self.read.get_data(api_name)
-
headers = self.read.get_headers(api_name)
-
response = self.run.run_main(method, url, headers, data)
-
-
# 把token值写到配置文件access_token.yml中,供其他接口调用
-
write_token(response)
-
-
print(json.dumps(response, indent=
2, ensure_ascii=
False, sort_keys=
False))
-
return response
-
except Exception
as e:
-
self.logger.info(
"接口访问出错啦~ %s" % e)
-
-
# 其他接口请求封装
-
def
send_request(
self, api_name):
-
try:
-
# 获取接口请求参数
-
method = self.read.get_method(api_name)
-
url = self.read.get_url(api_name)
-
headers = self.read.get_headers(api_name)
-
# 区分Get和Post方法
-
if method ==
"Get":
-
response = self.run.run_main(method, url, headers)
-
elif method ==
"Post":
-
data = self.read.get_data(api_name)
-
response = self.run.run_main(method, url, headers, data)
-
# print(json.dumps(response, indent=2, ensure_ascii=False, sort_keys=False))
-
-
print(json.dumps(response, indent=
2, ensure_ascii=
False, sort_keys=
False))
-
print(response[
"success"])
-
return response
-
except Exception
as e:
-
self.logger.info(
"接口访问出错啦~ %s" % e)
-
-
# 获取预期结果,方便断言时直接使用
-
def
get_expect(
self, api_name):
-
try:
-
# 获取配置文件中的预期结果
-
expect = self.read.get_expected(api_name)
-
# print(expect)
-
return expect
-
except Exception
as e:
-
self.logger.info(
"获取预期结果出错啦~ %s" % e)
4. 使用Pytest管理测试用例
1)在执行所有用例之前先执行登录接口,获取token。所以把登录接口的请求放在conftest.py文件中
-
#!/usr/bin/env python
-
# -*- coding:utf-8 -*-
-
# fileName: conftest.py
-
from API.all_api
import AllApi
-
import pytest
-
from common.get_log
import get_log
-
logger = get_log()
-
-
# 在执行所有用例之前先执行登录接口,获取token
-
@pytest.fixture(scope="session")
-
def
init_token():
-
# 正确邮箱/手机号和密码登录
-
logger.info(
"\n ============================= 在所有用例执行之前,生成token =============================")
-
all_login = AllApi()
-
all_login.login(
"login_sandbox_phoneNumber")
2) 把每个功能模块封装成一个类,每个用例封装成一个方法。例如资产模块的用例如下:
-
#!/usr/bin/env python
-
# -*- coding:utf-8 -*-
-
# fileName: test_asset.py
-
from API.all_api
import AllApi
-
import pytest
-
from common.get_log
import get_log
-
logger = get_log()
-
-
-
# 资产模块的测试用例
-
@pytest.mark.usefixtures("init_token")
-
class
TestAsset(
object):
-
@pytest.fixture(scope="class")
-
def
init_asset(
self):
-
logger.info(
"\n ==============================【资产】测试用例开始 ==============================")
-
all_request = AllApi()
-
return all_request
-
-
@pytest.mark.parametrize("api_name", ["asset_main"])
-
def
test_asset_main(
self, api_name, init_asset):
-
print(
"\n 用例名称:获取资产列表信息\n")
-
res = init_asset.send_request(api_name)
-
expected = init_asset.get_expect(api_name)
-
# 断言1:success的值为true
-
assert res[
'success'] == expected[
'success'],
"success的值为: %s" % res[
'success']
-
-
-
if __name__ ==
"__main__":
-
# pytest.main(['-v', 'test_asset.py'])
-
pytest.main([
'-v',
'test_asset.py',
'--html=report/asset_report.html',
'--self-contained-html'])
5. 添加日志模块
1)使用配置文件配置loggers、handlers和formatters(日志器、处理器和格式器),文件名称是log.conf
-
[
loggers]
-
keys=root,main
-
[
logger_root]
-
level=DEBUG
-
handlers=consoleHandler,fileHandler
-
[
logger_main]
-
level=DEBUG
-
qualname=main
-
handlers=fileHandler
-
[
handlers]
-
keys=consoleHandler,fileHandler
-
[
handler_consoleHandler]
-
class=StreamHandler
-
level=DEBUG
-
formatter=fmt
-
args=(sys.stdout,)
-
[
handler_fileHandler]
-
class=logging.handlers.RotatingFileHandler
-
level=DEBUG
-
formatter=fmt
-
args=(
'../log/Wallet_API_Test.log',
'a')
-
[
formatters]
-
keys=fmt
-
[
formatter_fmt]
-
format=%(asctime)s - %(filename)s - %(levelname)s - [line:%(lineno)d] - %(message)s
2)使用fileConfig()函数读取日志配置文件
-
# fileName: get_log.py
-
import logging.config
-
import logging
-
-
-
# 读取日志配置文件
-
def
get_log():
-
con_log =
"../configs/log.conf"
-
logging.config.fileConfig(con_log)
-
log = logging.getLogger()
-
return log
6. 生成测试报告
1)安装pytest-html插件。也可以使用allure插件生成测试报告,但是如果pytest版本过高,就无法识别allure插件
2)添加“--self-contained-html”可以整合样式文件到html文档中,方便之后发送测试报告到邮箱
pytest.main(['-v', '--html=report/all_report.html', '--self-contained-html'])
7. 测试报告发送至邮箱
-
#!/usr/bin/env python
-
# -*- coding:utf-8 -*-
-
# fileName: run_email.py
-
from email.mime.text
import MIMEText
-
from email.mime.multipart
import MIMEMultipart
-
from email.header
import Header
-
import smtplib
-
-
-
# 定义发邮件
-
def
send_mail(
file_path):
-
-
f =
open(file_path,
'rb')
-
mail_body = f.read()
-
f.close()
-
-
smtpserver =
'smtp.qq.com'
-
# 设置登录邮箱的账号和授权密码
-
user =
'XXX@qq.com'
-
password =
""
-
sender =
'XXX@qq.com'
-
# 可添加多个收件人的邮箱
-
receives = [
'XXX@qq.com']
-
# 构造邮件对象
-
msg = MIMEMultipart(
'mixed')
-
# 定义邮件的标题
-
subject =
'接口自动化测试报告'
-
# HTML邮件正文,定义成字典
-
msg[
'Subject'] = Header(subject,
"utf-8")
-
msg[
'From'] = sender
-
msg[
'To'] =
','.join(receives)
-
# 构造文字内容
-
text_plain = MIMEText(
"附件是最新的接口自动化测试报告,请查看",
'html',
'utf-8')
-
msg.attach(text_plain)
-
# 构造附件
-
text_attr = MIMEText(mail_body,
'base64',
'utf-8')
-
text_attr[
"Content-Type"] =
'application/octet-stream'
-
text_attr[
'Content-Disposition'] =
'attachment; filename = "test.html"'
-
msg.attach(text_attr)
-
-
# 邮箱设置时勾选了SSL加密连接,进行防垃圾邮件,SSL协议端口号要使用465
-
smtp = smtplib.SMTP_SSL(smtpserver,
465)
-
# 向服务器标识用户身份
-
smtp.helo(smtpserver)
-
# 向服务器返回确认结果
-
smtp.ehlo(smtpserver)
-
# 登录邮箱的账号和授权密码
-
smtp.login(user, password)
-
-
print(
"开始发送邮件...")
-
# 开始进行邮件的发送,msg表示已定义的字典
-
smtp.sendmail(sender, receives, msg.as_string())
-
smtp.quit()
-
print(
"已发送邮件")
-
-
-
if __name__ ==
"__main__":
-
report =
"../testcase/report/report.html"
-
send_mail(report)
8. 优化动态数据的存储
把多个登录账号信息等动态数据放到excel表格中,使用xrld和xlutils库对表格进行读取和写入。利用excel存储测试数据,实现测试数据和测试脚本的分离,方便测试数据的维护
9. 项目目录
一直在更新中,文件名可能与其他地方不一致
二、上传代码到github
1. 创建git仓库:进入项目的根目录,比如项目名是:Test,则进入Test文件夹下
命令:git init
2. 将项目内所有文件添加至仓库中
命令:git add .
3. commit到仓库
命令:git commit -m '首次提交'
4. 到github上创建Repositories后,复制仓库的地址,比如:https://github.com/XXX/Test.git
5. 将本地仓库关联到github上(很重要)
命令:git remote add origin https://github.com/XXX/Test.git
6. 上传代码到github远程仓库:其中-u是把本地master分支和远程master分支关联起来,以后再push时不需要加上-u
命令:git push -u origin master
7. 若再次上传,则要执行这三步:
1)把Test中的文件都添加进来:git add .
2)提交暂存区的文件,其中message是简要说明这次提交的信息:git commit -m "message"
3)把本地文件推送到github上(有时候需要输入github账号和密码): git push origin master
8. git常用命令,可参考文章git常用命令结合若干个使用场景
9. 项目git地址:(备注:代码写的不够专业,一直在优化改进中。有兴趣的可以一起讨论)
https://github.com/ChangYixue/Test.git
三、运行测试用例的两种方法
1. 命令行模式
1)先进入测试用例文件所在目录,然后使用“pytest”命令执行该目录下所有的测试用例文件;
2)也可以指定某一个测试用例文件,直接在pytest后面加上文件名称即可,比如“pytest test_asset.py”
3)执行”某文件下的类中所有用例方法“或者”某方法“,分别用命令”pytest 文件名::类名“和”pytest 文件名::类名::方法名“
2. 在python脚本中调用pytest
-
if __name__ ==
"__main__":
-
# 若存在多个标记,则用or连接,形成一个测试集,比如'-m=assetList or test or allLists'
-
pytest.main([
'-v',
'test_asset.py',
'-m=test'])
四、重构接口自动化测试框架
1. 背景:公司开发人员帮忙重构了脚本,使配置测试环境、维护测试数据、取接口信息和处理接口依赖等更加灵活
2. 重构后的框架简介请移步:Python+Pytest+tox 接口自动化测试框架
未完待续......