知识点--实战! 自动测试 Restful API
- 如何由 User Story 转换为 Robot Framework 测试案例
- 自动化測試 Restful API CRUD (Create/Read/Update/Delete)
- 判读测试报告
测试对象:基于 Restful API 之学生資訊查询系统
在功能开发前撰写的 User Story 如下图,本实验步骤就是要在不知道实作前提下完成所有测试实例。
实验步骤
1. 下载受测程序及测试资料
# 拉取受测程序执行档
$ wget http://labfile.oss.aliyuncs.com/courses/905/search-student-api
# 拉取测试资料
$ wget http://labfile.oss.aliyuncs.com/courses/905/students.json
# 将受测程序改为可执行
$ chmod +x search-student-api
-
受测程序是基於 Golang 开发的 Restful API 服务器,实作下列方法:
-
GET /students
: 列出全校学生 -
GET /students?search=Alice
: 取得名为 Alice 学生的资料 -
POST /students
: 将 JSON Array 格式的资料传入资料库 -
PUT /student/1
: 将 ID=1 的学生设定为毕业状态
-
-
回传资料皆以 JSON 格式显示, 包含下列键值:
- Code: 错误代码
- 0: 正常
- 1: 输入资料格式错误
- 2: 学生已经毕过业,无法重复设定
- 3: 找不到此学生
- Message: 错误讯息
- Graduated: 已经毕业学生资讯的 JSON Array
- UnGraduated: 尚未毕业学生资讯的 JSON Array
- Code: 错误代码
{
"Code": 0,
"Message": "success",
"Graduated": [{
"Id": 2,
"Name": "Bob",
"Graduated": true
}],
"Ungraduated": [{
"Id": 1,
"Name": "Alice",
"Graduated": false
}, {
"Id": 3,
"Name": "Cid",
"Graduated": false
}]
}
- 测试资料
测试资料里包含三个学生,每个学生都有编号、名字、毕业状态三个资讯,其中只有 Bob 是毕业生。下面将用这个档案完整执行所有的测试案例。
$ cat students.json
[
{
"Id": 1,
"Name": "Alice",
"Graduated": false
},{
"Id": 2,
"Name": "Bob",
"Graduated": true
},{
"Id": 3,
"Name": "Cid",
"Graduated": false
}
]
2. 执行 search-student-api
程序
服务器默认监听 port 8080,可接受三种 HTTP 方法,按 ctrl-c
可跳出程序,在这个实验里我们维持程序在前台执行以便观察测试代码如何跟服务器沟通。
$ ./search-student-api
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /students --> api.GetStudentsHandler (3 handlers)
[GIN-debug] PUT /student/:id --> api.GraduateStudentHandler (3 handlers)
[GIN-debug] POST /students --> api.AddStudentsHandler (3 handlers)
3. 开启另一个 Xfce 终端,开始编写自动测试程序
$ vim search-students.robot
代码解说:
- line 4-5: 设定套用到整个测试套件的变量,由于我们会读取本机的档案所以需要库
OperatingSystem
,而RequestsLibrary
是专门用来处理 HTTP Request 的库 - line 6-7: 产生一个可以让测试套件 (Test Suite) 使用的 HTTP 对话 (Session),并将其命名为
api
,而Run Keywords
关键字的用法可参阅官网文档,数个关键字中间可用AND
串接 - line 10-13: 定义整个测试套件 (Test Suite)会使用到的变量,注意这边定义的变量也可以在
Settings
区块使用,如变量${hostport}
4. 撰写测试关键字
关键字一般都由工程师撰写,目的是实作测试逻辑或将某些功能抽出来重复利用
- HTTP Code Should Equal: 确认伺服器回应的 HTTP 状态码是否如预期
- line 17:
[Arguments]
跟一般函式的输入变量相同,其中${resp}
以及${http_code}
都是必要值,而${msg}
则可以不需传入,若没有值传入的话它会被带入预设值${EMPTY}
,也就是 python 的空字串 - line 18: Should Be Equal 系列的关键字是我们最常用来判断测试结果是否如预期的工具,这边会先将
${resp.status_code}
以及${http_code}
两个变量先转为字串格式再比较是否相等,如果不相等时会印出${msg}
里面的客制化资讯。
- line 17:
- Insert Test Data: 读取档案并将资料输入资料库
- line 21: Get File 关键字可用来读取本地档案并将其全部内容放入变量
${json_string}
- line 22: 发出
POST /students
并将${json_string}
传递到服务器 - line 23: 预期服务其回传的 HTTP 代码应该是
201 Created
- line 21: Get File 关键字可用来读取本地档案并将其全部内容放入变量
- Get JSON Object From File: 输入档名,读取档案内容并将其转为 JSON Object
- Let Student Graduate By Id: 将某 Id 的学生状态设为毕业
- line 33: 利用 Put Request 关键字发送
PUT /student/{id}
方法到服务器,例如PUT /student/1
表示将学生 Alice 设为毕业。 - line 34: 预期服务其回传的 HTTP 代码应该是
202 Accepted
,表示服务器让学生毕业
- line 33: 利用 Put Request 关键字发送
- Get All Students
- line 37-38: 呼叫
GET /students
并且预期回传 HTTP200 OK
- line 39-40:
{resp.json()}
RequestLibrary提供这个方便的方法把回传的字串转变成 python dictionary,可以直接用${json['Code']}
取值比对
- line 37-38: 呼叫
-
Get Student By Name
- line 45-46: 呼叫
GET /students?search={name}
方法,并且预期回传 HTTP200 OK
- line 49: 将伺服器回传值转变为 python dictionary 回传
- line 45-46: 呼叫
-
Is Student Graduated: 输入学生姓名并回传学生是否毕业
- line 53: 呼叫先前定义的关键字并且取得特定学生的资讯
- line 54: 如果
${json}
缺少Graduated
键值,代表此学生尚未毕业,只关键字回传${FALSE}
- line 55: 若
Graduated
键值里面含有此学生的姓名,代表他已经毕业
5. 撰写测试案例 - 身为用户,我能够列出所有在校学生,也能看到所有毕业学生
Robot Framework 另一个特色是测试案例可以取中文名称,可以轻松跟 User Story 做一对一映射。我们在这边呼叫 Get All Students
关键字,如果正确执行代表满足测试条件。
6. 撰写测试案例 - 身为用户,我能够利用姓名搜寻学生相关资讯
7. 撰写测试案例 - 身为用户,我能利用学生的ID将其设定为毕业状态,学生的毕业状态从此以后必须改变
8. 身为用户,我无法让一个学生毕业两次
由于我们已在上个测试案例里让 Alice 毕业,这边先确认 Alice 是否毕业以后再强制让她毕业一次,预期得到 HTTP 409 Conflict
以及code=2
的状态,${json['message']}
错误讯息也指出这个学生已经毕过业了。
执行测试及判读报告
Robot Framework 本身支援中文,但是为了在实验环境复现方便,我把测试案例全都翻译成英文,同学可以直接下载 robot
档案。
# 下载 search-students.robot
$ wget http://labfile.oss.aliyuncs.com/courses/905/search-students.robot
# 执行测试
$ robot search-students.robot
执行结果:
将 /home/shiyanlou/log.html
贴上 FireFox 网址列就可以看到此次的测试报告,点开每个测试案例可以看到详尽的执行步骤以及所花费时间。同学也可以自己试著把测试案例中的预期值改掉刻意让测试失败,看看会发生什么事。
结语
经过这个测试 Restful API 的实战经验,同学可以发现主要的逻辑代码都会在关键字定义好,将关键字堆叠起来佐以一些条件判断就可以写出变化多端的测试,测试案例的 Stateful 特性可以让同学在一个测试套件里面经由数个测试案例描述一连串的使用者体验。 Robot Framework 还可以支援更多种应用的自动测试,有机会再跟大家谈谈吧!