作者:罗格林
链接:
https://my.oschina.net/greenlaw110/blog/1821431
老码农在上一篇博客 给出了如何从头开始创建一个 自带自动化测试工具的 RESTful 服务项目的例子. 今天我们在这个简单例子上做延伸, 把这个例子改写为一个简单的 TODO Task 应用. 该应用会提供以下服务端口:
GET /todos - 返回所有的 TODO 项
GET /todos/?q=? - 查询 TODO 项, 所有描述符合 q 参数的 TODO 项都会被返回
GET /todos/{id} - 返回指定 ID 的 TODO 项
POST /todos - 添加一条 TODO 项
DELETE /todos/{id} - 删除指定 ID 的 TODO 项
1. 创建项目
下面开始创建初始项目:
下面我们将项目用 Intellij IDEA 打开. (推荐使用 IDEA 开发 Act 应用, 社区版足够使用了)
2. 加入数据库访问插件依赖
可以删除掉项目创建的 Service.java 文件. 然后在 pom.xml 中加入一下依赖:
act-eclipselink 使用 EclipseLink 提供数据库访问服务. 也可以换为 act-hibernate, 对我们这个 todo-service 的开发没有任何变化.
3. 加入 Todo 服务相关类
现在可以在项目中创建我们的 Model 类 - Todo:
注意, 这里实现了 SimpleBean 框架会自动为 Todo 创建 Getter 和 Setter 方法. 在 Todo 之外的地方调用 todo.desc = "abc" 会被自动更换为 todo.setDesc("abc"), 而 String s = todo.desc 则会被自动更换为 String s = todo.getDesc().
下面创建 Todo 的服务类 TodoService:
4. 启动并试用服务
好了, 开发工作搞定. 现在运行起来试试. 有两种方法运行程序.
在 IDE 里面运行:
通过 mvn 命令在控制台运行:
选择任何一种方式, 把应用跑起来之后, 我们用 httpie 来试试我们的 Todo 服务:
4.1 创建一个 todo 项
4.2 获取 todo 列表
4.3 获取 id 为 1 的 todo 项
4.4 删除 id 为 1 的 todo 项, 并验证
到此基本上我们的实现部分就完成了, 同时也手工做了测试. 看看代码统计:
所有 Java 代码加起来也就 58 行, 虽说是一个非常粗糙的 Todo 服务, 也算是麻雀虽小五脏俱全了.
5. 自动测试的实现
下面开始进入肉戏了. 我们刚刚使用了 httpie 对服务进行了测试, 貌似也很简单, 可这毕竟需要人工介入啊. 没听葛先生说过二十一世纪人工最贵吗? 怎么能把这么贵的人工浪费在重复性的工作上面. 更重要的问题是人工在这种重复性劳作上远远不如机器可靠, 如果没有自动化测试的保障, 即便是大牛也不敢随便对代码动刀子搞搞重构之类的高级手术.
那自动化测试怎么搞, 这是一个问题. 老码农也思考了很多年. 最初的想法是复制 Spring 的招数, 搞一个 ActRunner之类的东西让开发人员能在 JUnit 框架下跑对 Act 应用的测试. 可当老码农看到 3.4.5. Meta-Annotation Support for Testing 一节的时候已经被吓傻了, 仅仅是为了支持测试可能会用到的注解就已经是这番模样了:
这完全和 Act 的核心理念背道而驰啊. 欲破珍珑棋局, 必须跳出棋盘, 自动测试也不是一定要在 JUnit 框架下进行. 多年寻寻觅觅找不到满意的方案, 这思路一开, act-e2e 插件便横空出世.
5.1 act-e2e 简介
act-e2e 是老码农为 Act 应用开发提供的自动化测试插件, 其设计目的主要有一下几点:
提供端到端测试支持, 换句话说, 测试通过 HTTP 协议向应用发送请求, 并接受应用传回的 HTTP 响应, 并验证响应的内容
测试脚本应该易于编写. 用户只需要定义每次交互的请求数据以及响应数据的验证方式, 完全无需考虑应用内部实现
测试环境治理. 这包括:
运行测试场景之前对数据库清除
运行测试场景之前加载指定数据文件到数据库中
数据文件 (Fixture) 通过 YAML 格式定义, 包括数据项之间的关联关系
测试场景的依赖管理, 某些测试场景可能被其他场景依赖, 比如用户登录这个场景就可以被很多其他测试场景依赖. 而用户登录自身又可能依赖与用户注册场景.
其他工具, 比如共享的请求模板, 如果大部分请求都需要添加某些 HTTP 头, 可以定义请求模板, 并被其他请求引用.
5.2 Scenarios 文件
定义 Scenarios.yml 文件内容是使用 act-e2e 进行自动化测试的核心活动. 这个文件的结构如下:
在本文中我们不会详细罗列整个 Scenarios 文件的语法结构, 而是通过对 Todo 服务进行自动化测试来介绍 Scenarios 文件的用法.
5.3 为 Todo 服务实现自动测试
打开 src/main/resources/e2e/scenarios.yml 文件, 清除其中的内容, 添加针对 TODO 服务的测试脚本. Todo 服务足够简单, 一个场景足够涵盖基本测试, 我们首先加入场景声明:
接下来加入第一个交互:
分解一下该交互的定义. 首先是请求:
请求的元素主要为:
method: HTTP 方法, 可以为 GET/POST/DELETE/PUT/PATCH/HEAD
url: 测试服务的 URL
params: 请求发送的参数
headers: 请求头. 这个部分在本文中不会使用
本例中响应的定义为:
这个定义的意思是, 这个响应应该是一个 {"result": ...} JSON 结构. 该定义来自 Todo 服务的下面的服务端口:
从代码看来, 返回的应该是一个整数类型的值. 但因为我们前面定义了整个控制器都是 @JsonView, 也就是任何返回都应该是合法的 JSON 结构. 对于无结构的值, 我们使用 result 来包裹返回值. 因此, 我们的响应定义中有 result: -exists: true 这样的验证.
加入第一个交互之后我们就可以试试 e2e 了, 打开浏览器, 导航到 localhost:5460/~/e2e, 会看到定义的测试以及运行情况:
这个交互定义有个地方值得注意: url: /todos/${last:result}, 这个的意思是取上一个交互对象的 result 引用的值填充到 /todos/ 后面, 也就是说 url 最后会是 /todos/{id}, 而 {id} 是上次添加 Todo 项生成的 id.
对于 response 的定义则是检测是否 JSON 数据中的 desc 为 Task A.
回到浏览器, 按 F5 刷新:
这个交互定义中比较有趣的是响应的验证定义. 其含义是 json 数据是一个数组, 其中有一个元素, 第一个元素的 desc 为 Task A
刷新浏览器得到:
这个和前面的交互 [5.3.3 获得 Todo 列表] 非常接近, 唯一不同的地方是 GET 请求多了请求参数: q=A. 表示查询含有 A 字母的 Todo 项.
再次刷新浏览器:
Oops, 怎么最后的交互没有通过. 回到后台, 发现这样的错误:
这应该是希望返回数组有一个元素, 但实际返回数组没有元素. 仔细检查一下, 应该是在 A 前面加上 % 才行, 更改我们的 scenarios.yml 文件, 将 q: A 改成 q: %A, 之后再刷新浏览器:
貌似错误更加严重了, 原来 % 是 yaml 的保留字符, 需要用引号括起来, 即 q: %A 变为 q: '%A'. 最后终于全部搞定了:
5.4 在 CI 中集成 e2e 测试
我们上面的过程都使用了浏览器访问 /~/e2e 来完成测试. 这个对于开发调试 sceanrios.yml 测试脚步非常方便. 但是对于 CI 集成自动测试过程就不友好了. 不用担心, Act 的 maven 插件可以帮忙解决这个问题:
获得的结果是:
这里还需要一些改进, 方便 CI 工具更容易判断测试是否通过. 老码农会在以后的版本中持续改进对端到端自动测试的支持.
另外在操作过程中有可能出现一些异常现象, 需要重启动应用. 这个问题老码农已经提交 issue 报告了. 将会在以后的版本中修复.
本文讲述的项目代码在 gitee 上有完整版本, 有兴趣可以参考:
https://gitee.com/greenlaw110/todo-service/
参与「程序员专属信用卡」调研
让我们打造最适合程序员的信用卡!
美食、购物、航旅…优惠折扣等着你哦~
扫码参与投票