前言
做为开发人员,测试这个领域很少触及,但是最近心血来潮,想了解一下自动化测试,然后就先从接口测试开始了。本人是java党,py刚入门,代码写得很粗糙,忘各位大神多多指点。
准备工作
本文以登录接口为例。
接口文档
- 用户登录
请求URL:
{{baseUrl}}/ums/user/login
请求方法:
- POST
请求头
- application/json
请求参数:
| 参数名 | 必选 | 类型 | 说明 |
|---|---|---|---|
| userName | 是 | string | 用户名 |
| password | 是 | string | 密码 |
参数样例:
{
"userName": "admin",
"password": "666666"
}
返回样例
{
"msg": "ok",
"code": 0,
"data": {
"userId": "1",
"userName": "mldong",
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHQiOiJ7fSIsImlzcyI6Im1vbGUiLCJjaGFubmVsIjoiYWRtaW4iLCJleHAiOjE1ODg1MTIyNzUsImlhdCI6MTU4ODUwODY3NSwidXNlcklkIjoiMSIsInVzZXJuYW1lIjoibWxkb25nIn0.2AixzH5Dy_I11Rla4uylhxsMQIBYVDn9eYLK619d9u0"
}
}
状态码说明
| 状态码 | 说明 |
|---|---|
| 0 | 成功 |
| 81009001 | 用户不存在 |
| 81009002 | 用户名或密码错误 |
| 81009003 | 登录次数太多 |
| 81009004 | 用户已被锁定 |
| 99990001 | 参数不合法 |
用例文档
测试用例编写参考于接口文档,接口自动化也是基于测试用例,所以在做接口自动化前,就得先制定测试用例文档模板。
| id | name | description | url | method | data | contentType | rule | expectedResult |
|---|---|---|---|---|---|---|---|---|
| L001 | 登录 | 用户名密码正确 | ums/user/login | post | { "userName":"mldongdemo", "password":"mldongdemo" } |
application/json | default | 0 |
| L002 | 登录 | 用户不存在 | ums/user/login | post | { "userName":"mldong6666", "password":"mldongdemo" } |
application/json | default | 81009001 |
| L003 | 登录 | 用户名或密码错误 | ums/user/login | post | { "userName":"mldongdemo", "password":"mldong6666" } |
application/json | default | 81009002 |
| L004 | 登录 | 登录次数太多 | ums/user/login | post | { "userName":"mldongdemo", "password":"mldong6666" } |
application/json | default | 81009003 |
| L005 | 登录 | 用户已被锁定 | ums/user/login | post | { "userName":"mldongdemo", "password":"mldongdemo" } |
application/json | default | 81009004 |
| L006 | 登录 | 参数不合法 | ums/user/login | post | { "userName":"5599090", "password":"mldong" } |
application/json | default | 99990001 |
用例模板说明
| 属性 | 说明 |
|---|---|
| id | 用例编号 |
| name | 用例名称 |
| description | 用例说明 |
| url | 请求地址 |
| method | 请求方法 |
| data | 请求参数 |
| contentType | 请求类型 |
| rule | 校验规则 |
| expectedResult | 预期结果(状态码) |
处理流程
- 第一步:加载配置信息
- 第二步:读取用例模板数据
- 第三步:构造请求参数
- 第四步: 发送接口请求
- 第五步:解析接口返回内容
- 第六步:使用校验规则,输出检验结果
- 第七步:组装测试结果数据
- 第八步:生成测试报告
开始编码
工程目录
├── config
└── app.yml # 配置文件
├── usecase # 测试用例目录
├── login.yml # yaml定义的测试用例
└── login.xlsx # xlsx定义的测试用例
├── templates # 测试报告模板目录
└── index.html # 测试报告模板
├── testport # 测试报告目录
└── result.html # 测试报告
├── main.py # py脚本
└── requirements.txt # 依赖库
config/app.yml
# 工程名称
name: moletest
# 工程说明
description: 接口自动化测试
# 版本号
version: V1.0
usecaseType: yaml # 用例类型(yaml/xlsx)
# 测试用例目录(暂时不处理,使用绝对路径)
usecaseDir: F:\\mldong\\python-workspace\\test\\usecase
# 测试报告目录(暂时不处理,使用绝对路径)
testreport: F:\\mldong\\python-workspace\\test\\testreport
baseUrl: http://demo.mldong.com/api/
usecase/login.yml
usecaseList:
- id: L00001 # 用例编号
name: 登录 # 用例名称
description: 用户名密码正确 # 用例说明
url: ums/user/login # 接口地址
method: post # 请求方法
data: '{"userName": "mldong","password":"mldong@666"}' # 请求参数
contentType: application/json # 参数类型
rule: default # 校验规则
expectedResult: 0 # 预期结果code=0
- id: L00002 # 用例编号
name: 登录 # 用例名称
description: 用户名密码错误 # 用例说明
url: ums/user/login # 接口地址
method: post # 请求方法
data: '{"userName": "mldong","password":"123456666"}' # 请求参数
contentType: application/json # 参数类型
rule: default # 校验规则
expectedResult: 81009002 # 预期结果code=81009002
usecase/login.xlsx
略。可参考用例模板制作
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试报告</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<table class="table">
<tr>
<th>用例编号</th>
<th>用例名称</th>
<th>用例说明</th>
<th>接口地址</th>
<th>请求参数</th>
<th>测试结果</th>
</tr>
{% for item in data %}
<tr>
<td>{{ item['id'] }}</td>
<td>{{ item['name'] }}</td>
<td>{{ item['description'] }}</td>
<td>{{ item['url'] }}</td>
<td>{{ item['data'] }}</td>
<td>{{ item['result'] }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
requirements.txt
certifi==2020.4.5.1
chardet==3.0.4
idna==2.9
Jinja2==2.11.2
MarkupSafe==1.1.1
numpy==1.18.3
pandas==1.0.3
python-dateutil==2.8.1
pytz==2020.1
PyYAML==5.3.1
requests==2.23.0
six==1.14.0
urllib3==1.25.9
xlrd==1.2.0
xlwt==1.3.0
main.py
import requests,yaml,os,json
from jinja2 import FileSystemLoader,Environment
import pandas as pd
def loadConfig():
"""
加载配置信息
"""
with open('config/app.yml','r',encoding='utf-8') as f:
config = yaml.load(f,Loader=yaml.FullLoader)
return config
def loadYamlUsecase(config):
"""
加载yaml用例
"""
res = []
dir = config['usecaseDir']
for root,dirs,files in os.walk(dir):
for file in files:
if os.path.splitext(file)[1] in ['.yaml','.yml']:
with open(os.path.join(root,file),'r',encoding="utf-8") as f:
config = yaml.load(f,Loader=yaml.FullLoader)
usecaseList = config['usecaseList']
for item in usecaseList:
res.append(item)
return res
def loadXlsxUsecase(config):
"""
加载excel用例
"""
res = []
dir = config['usecaseDir']
for root,dirs,files in os.walk(dir):
for file in files:
if os.path.splitext(file)[1] in ['.xlsx']:
df = pd.read_excel(os.path.join(root,file))
data = df.to_dict('records')
for item in data:
res.append(item)
return res
def processUsecase(usecase,config):
"""
执行测试用例-单个
"""
res = usecase
headers = {"Content-Type": usecase["contentType"]}
baseUrl = config['baseUrl']
url = usecase['url']
if url.startswith('http') == False:
url = baseUrl+url
data = usecase['data']
r = requests.post(url,data=data,headers=headers)
body = r.text
print(f"请求地址:{r.url}")
print(f"请求头:{r.headers}")
print(f"请求体:{r.request.body}")
print(f"响应时间:{r.elapsed.total_seconds()}s")
print(f"返回结果:{r.text}")
bodyJson = json.loads(body)
res['result']=bodyJson['code'] == usecase['expectedResult']
res['body']=body
res['totalSeconds']=r.elapsed.total_seconds()
return res
def processUsecaseList(usecaseList,config):
"""
执行测试用例-遍历
"""
res = []
for usecase in usecaseList:
res.append(processUsecase(usecase,config))
return res
def buildTestReport(data,config):
"""
生成测试报告
"""
env = Environment(loader=FileSystemLoader('templates'))
template = env.get_template('index.html')
html = template.render({
"data": reportData
})
with open(config['testreport']+"/result.html", 'w',encoding="utf-8") as f:
f.write(html)
return 1
if __name__ == '__main__':
config = loadConfig()
usecaseType = config['usecaseType']
usecaseList = []
if usecaseType == 'yaml':
usecaseList = loadYamlUsecase(config)
elif usecaseType == 'xlsx':
usecaseList = loadXlsxUsecase(config)
else:
print("用例不存在或不支持此类型")
if len(usecaseList) == 0:
print("用例不存在")
else:
reportData = processUsecaseList(usecaseList,config)
buildTestReport(reportData,config)
执行命令
python main.py
常用命令说明
- 安装指定库
pip3 install requests -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
- 生成依赖
pip3 freeze > requirements.txt
- 安装依赖
pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com