文档章节

使用 maven 生成一个支持端到端自动测试的 RESTful 服务项目脚手架

罗格林
 罗格林
发布于 05/13 21:02
字数 2572
阅读 2699
收藏 67

额外的话: 我会非常感激如果您在读本文的时候 Follow 文中的操作步骤在你的环境中实践本文所讲内容, 我保证这个过程不会非常复杂, 即便加上你研究代码的时间也不需要超过 15 分钟

和传统后端页面生成技术相较, RESTful 数据服务专注与数据逻辑, 而将数据呈现完全交给前端应用. 这样做可以让后端开发更加单纯, 而且更容易测试. 本文将讲述如何使用 maven 生成一个支持端到端自动测试的 RESTful 服务的项目脚手架.

1. 准备环境

如果你打算跟随本文在你的开发机上试验, 需要一下环境:

  1. Java SDK 1.7+
  2. maven 3.5+

如果有 IDE 就更好. 作者推荐使用 IntelliJ IDEA, 可以使用社区版, 完全免费.

2. 定义项目包和应用名称

项目包 (package) 和应用名称 (artifact) 是你的项目在 Java 依赖体系中的坐标, 即使你的项目无需被其他项目引用, 也应该给出简单明确的包和应用名字, 以便于沟通交流.

这里为我们即将生成的应用给出下面的包名和应用名:

  • 包名: demo.restful
  • 应用名: SimpleService

3. 生成项目脚手架

我们使用 actframework 的 archetype-simple-restful-service 来生成项目脚手架. 在命令行下

mvn archetype:generate -B \
    -DgroupId=demo.restful \
    -DartifactId=simple-service \
    -DarchetypeGroupId=org.actframework \
    -DarchetypeArtifactId=archetype-simple-restful-service \
    -DarchetypeVersion=1.8.8.6

运行以上命令大致可以看到下面的 log 信息:

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] Archetype repository not defined. Using the one from [org.actframework:archetype-simple-restful-service:1.8.7.3] found in catalog remote
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: archetype-simple-restful-service:1.8.8.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: demo.restful
[INFO] Parameter: artifactId, Value: simple-service
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: demo.restful
[INFO] Parameter: packageInPathFormat, Value: demo/restful
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: demo.restful
[INFO] Parameter: groupId, Value: demo.restful
[INFO] Parameter: artifactId, Value: simple-service
[INFO] Executing META-INF/archetype-post-generate.groovy post-generation script
[INFO] Project created from Archetype in dir: /tmp/1/simple-service
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.984 s
[INFO] Finished at: 2018-05-13T22:15:58+10:00
[INFO] Final Memory: 25M/305M
[INFO] ------------------------------------------------------------------------

4. 项目结构与内容

使用 tree 命令来查看项目结构:

simple-service/
├── pom.xml
├── run_dev
├── run_dev.bat
├── run_e2e
├── run_e2e.bat
├── run_prod
└── src
    ├── main
    │   ├── java
    │   │   └── demo
    │   │       └── restful
    │   │           ├── AppEntry.java
    │   │           └── Service.java
    │   └── resources
    │       ├── conf
    │       │   ├── app.properties
    │       │   ├── prod
    │       │   │   └── app.properties
    │       │   └── uat
    │       │       └── app.properties
    │       ├── demo
    │       │   └── restful
    │       ├── e2e
    │       │   └── scenarios.yml
    │       ├── logback.xml
    │       └── rythm
    │           └── demo
    │               └── restful
    └── test
        └── java
            └── demo

4.1 项目启动入口: AppEntry

我们看到生成的项目中有一个 AppEntry.java 文件, 打开该文件, 其内容为:

public class AppEntry {

    public static void main(String[] args) throws Exception {
        Act.start();
    }

}

这是一个很简单的 Java 类, 其作用是启动 ActFramework.

4.2 服务类: Service

打开本项目中的另一个 Java 文件 Service.java:

public class Service {

    @GetAction("hello")
    public String hello(@DefaultValue("World") String who) {
        return "Hello " + who;
    }

    @GetAction("date")
    public DateTime date() {
        return DateTime.parse("2016-03-09");
    }

}

代码很简单, 提供了两个 RESTful 数据服务端口:

4.2.1 GET /hello

当访问这个服务端口的时候, 应用返回 "Hello xxx" 形式的内容, 其中 xxx 是通过请求参数 who 传递的, 如果请求没有提供这个参数, 则使用默认值 World, 即, 返回 "Hello World" 字串.

4.2.2 GET /date

这个服务端口不接受参数, 直接返回一个日期数据

4.3 运行脚本

maven 为项目生成了几个可执行脚本文件:

  • run_dev - 以开发模式启动项目
  • run_dev.bat - run_dev 的 windows 版本
  • run_e2e - 运行端到端测试
  • run_e2e.bat - run_e2e 的 windows 版本
  • run_prod - 以产品模式启动项目

我们没有提供 run_prod 脚本的 windows 版本, 主要原因是 windows 没有默认安装支持解包 tar.gz 文件的命令行应用. 如果需要在 windows 下运行产品模式可以这样做:

  1. 运行 mvn clean package
  2. 到项目的 target/dist 目录下, 找到 tar.gz 文件, 使用第三方工具, 比如 7zip 等解包, 然后运行 run.bat 文件

5. 启动应用

下面我们使用 run_dev 脚本, 或者直接使用 mvn compile act:run 启动应用:

  __  ___         _        _       __   _   _         ___   _   _ 
 (_    |   |\/|  |_)  |   |_  __  (_   |_  |_)  \  /   |   /   |_ 
 __)  _|_  |  |  |    |_  |_      __)  |_  | \   \/   _|_  \_  |_ 
                                                                  
                          powered by ActFramework r1.8.8-RC4-aa2d4

 version: v1.0-SNAPSHOT-180513_2237
scan pkg: 
base dir: /tmp/1/simple-service
     pid: 26126
 profile: dev
    mode: DEV

     zen: Flat is better than nested.

2018-05-13 22:37:47,875 INFO  a.Act@[main] - loading application(s) ...
2018-05-13 22:37:47,885 INFO  a.a.App@[main] - App starting ....
2018-05-13 22:37:48,104 WARN  a.h.b.ResourceGetter@[main] - URL base not exists: META-INF/resources/webjars
2018-05-13 22:37:48,119 WARN  a.a.DbServiceManager@[main] - DB service not initialized: No DB plugin found
2018-05-13 22:37:48,741 WARN  a.m.MailerConfig@[main] - smtp host configuration not found, will use mock smtp to send email
2018-05-13 22:37:49,033 INFO  a.a.App@[main] - App[simple-service] loaded in 1148ms
2018-05-13 22:37:49,037 INFO  a.a.ApiManager@[jobs-thread-3] - start compiling API book
2018-05-13 22:37:49,055 INFO  o.xnio@[main] - XNIO version 3.3.8.Final
2018-05-13 22:37:49,143 INFO  o.x.nio@[main] - XNIO NIO Implementation Version 3.3.8.Final
2018-05-13 22:37:49,244 INFO  a.Act@[main] - network client hooked on port: 5460
2018-05-13 22:37:49,245 INFO  a.Act@[main] - CLI server started on port: 5461
2018-05-13 22:37:49,246 INFO  a.Act@[main] - app is ready at: http://192.168.1.2:5460
2018-05-13 22:37:49,246 INFO  a.Act@[main] - it takes 2149ms to start the app

项目启动之后可以从浏览器来访问. 首先来看 GET /hello 服务端口的情况:

image

再请求中加入一个 who 参数再试试:

hello-with-query-param

最后看看 GET /date 服务端口:

image

6. 测试

我们已经使用浏览器来验证项目可以正常运行, 也能得到期望的结果. 对于简单的应用来讲, 可以使用这种方式进行测试, 但随着项目的开发, 更多的服务端口会加入进来, 每次都这样来运行测试, 对开发测试人员来说是很大的负担. 下面我们先运行一下生成的 run_e2e 脚本, 或者也可以直接使用 maven 命令 mvn compile act:e2e:

Listening for transport dt_socket at address: 5005
  __  ___         _        _       __   _   _         ___   _   _ 
 (_    |   |\/|  |_)  |   |_  __  (_   |_  |_)  \  /   |   /   |_ 
 __)  _|_  |  |  |    |_  |_      __)  |_  | \   \/   _|_  \_  |_ 
                                                                  
                          powered by ActFramework r1.8.8-RC4-aa2d4

 version: v1.0-SNAPSHOT-180513_2248
scan pkg: 
base dir: /tmp/1/simple-service
     pid: 27429
 profile: e2e
    mode: DEV

     zen: Simple things should be simple, complex things should be possible.

2018-05-13 22:48:52,651 INFO  a.Act@[main] - loading application(s) ...
2018-05-13 22:48:52,661 INFO  a.a.App@[main] - App starting ....
2018-05-13 22:48:52,882 WARN  a.h.b.ResourceGetter@[main] - URL base not exists: META-INF/resources/webjars
2018-05-13 22:48:52,895 WARN  a.a.DbServiceManager@[main] - DB service not initialized: No DB plugin found
2018-05-13 22:48:53,580 WARN  a.m.MailerConfig@[main] - smtp host configuration not found, will use mock smtp to send email
2018-05-13 22:48:53,910 INFO  a.a.App@[main] - App[simple-service] loaded in 1249ms
2018-05-13 22:48:53,914 INFO  a.a.ApiManager@[jobs-thread-2] - start compiling API book
2018-05-13 22:48:54,017 INFO  o.xnio@[main] - XNIO version 3.3.8.Final
2018-05-13 22:48:54,032 INFO  o.x.nio@[main] - XNIO NIO Implementation Version 3.3.8.Final
2018-05-13 22:48:54,130 INFO  a.Act@[main] - network client hooked on port: 5460
2018-05-13 22:48:54,131 INFO  a.Act@[main] - CLI server started on port: 5461
2018-05-13 22:48:54,132 INFO  a.Act@[main] - app is ready at: http://192.168.1.2:5460
2018-05-13 22:48:54,133 INFO  a.Act@[main] - it takes 2332ms to start the app

Start running E2E test scenarios

================================================================================
HELLO SERVICE

a service says hello
--------------------------------------------------------------------------------
[PASS] send request to hello service without parameter
[PASS] send request to hello servcie with parameter specified
[PASS] send request to hello servcie with parameter specified and require JSON response
--------------------------------------------------------------------------------
It takes 0s to run this scenario.

================================================================================
DATE SERVICE

A service returns an important date in the history
--------------------------------------------------------------------------------
[PASS] send request to the service
[PASS] send request to the service and request response be JSON format
--------------------------------------------------------------------------------
It takes 0s to run this scenario.

2018-05-13 22:48:55,224 INFO  a.a.App@[jobs-thread-4] - App shutting down ....
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.874 s
[INFO] Finished at: 2018-05-13T22:48:55+10:00
[INFO] Final Memory: 26M/389M
[INFO] ------------------------------------------------------------------------

我们看到项目启动之后运行了 E2E 测试场景, 包括对 GET /helloGET /date 的 5 个测试用例, 并且都通过了测试. 下面打开 src/main/resources/e2e/scenarios.yml 文件来看看测试场景和用例是如何定义的:

Scenario(Hello Service):
  description: a service says hello
  interactions:
    - description: send request to hello service without parameter
      request:
        method: GET
        url: /hello
      response:
        text: Hello World # response text must be "Hello World"
    - description: send request to hello servcie with parameter specified
      request:
        method: GET
        url: /hello?who=ActFramework
      response:
        # this time we demonstrate how to verify text with a list of verifiers
        text:
          - eq: Hello ActFramework # value must be equal to "Hello ActFramework"
          - contains: ActFramework # value must contains "ActFramework"
          - starts: Hello # value must starts with "Hello"
          - ends: Framework # value must ends with "Framework"
    - description: send request to hello servcie with parameter specified and require JSON response
      request:
        json: true # specify accept type is application/json
        method: GET
        url: /hello?who=Java
      response:
        json: # treat result as a JSON object
          # act returns json result in `{"result": ...}` style
          result: Hello Java # result property of the JSON object must be "Hello World"

# Test Service#date() endpoint, which is available at `GET /date` endpoint
Scenario(Date Service):
  description: A service returns an important date in the history
  interactions:
    - description: send request to the service
      request:
        method: GET
        url: /date
      response:
        text:
          - after: 1997-05-11 # the returned date should be after date 1997-05-11
          - before: 13/May/2018 # the returned date should be before date 13/May/2018
    - description: send request to the service and request response be JSON format
      request:
        json: true
        method: GET
        url: /date
      response:
        json: # treat result as a JSON object
          # act returns json result in `{"result": ...}` style
          result:  # thus we will use `result` to fetch the date
            - after: 1997-05-11 # the returned date should be after date 1997-05-11
            - before: 13/May/2018 # the returned date should be before date 13/May/2018

这个文件还是很容易理解, 其内容基本是按照下面的方式组织的:

  1. 首先第一层定义测试场景 Scenario, 每个 Scenario 都会给出一个名字放进圆括弧中. 我们在文件中定义了两个 Scenario
    • Hello Service - 测试 GET /hello 端口
    • Date Service - 测试 GET /date 端口
  2. 场景 (Scenario) 下面定义一个或者多个交互 Interactions
  3. 对每个交互定义请求 Request 和 响应 Response
  4. 请求 Request 的内容包括方法和 URL, 也可以指定是否要求返回 JSON 格式的请求
  5. 响应 Response 则定义期望返回内容的验证

针对每个服务端口可以写一个或多个场景, 也可以在一个场景中顺序测试多个服务端口. 完全有应用自己来定义.

7. 总结

本文介绍了如何使用 maven archetype 来生成一个可测试 RESTful 数据服务项目的脚手架, 以及如何通过定义 e2e/scenarios.yml 文件来提供自动测试的功能. 希望能够对大家带来帮助.

参考:

© 著作权归作者所有

共有 人打赏支持
罗格林

罗格林

粉丝 272
博文 43
码字总数 41930
作品 4
其他
架构师
加载中

评论(12)

小马视频
小马视频
学习了
罗格林
罗格林

引用来自“polly”的评论

Non-resolvable parent POM for demo.restful:simple-service:1.0-SNAPSHOT: Could not find artifact org.actframework:act-starter-parent:pom:1.8.8.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 13, column 11

引用来自“罗格林”的评论

@polly 我已经发布 act-maven-archetypes 1.8.8.1 版了. 这个博客相关部分也已经够更新了

引用来自“polly”的评论

测试可行
:+1:

接下来你可以尝试修改服务端代码, 返回不同的值, 再运行一下 e2e 测试, 看看会发生什么变化
polly
polly

引用来自“polly”的评论

Non-resolvable parent POM for demo.restful:simple-service:1.0-SNAPSHOT: Could not find artifact org.actframework:act-starter-parent:pom:1.8.8.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 13, column 11

引用来自“罗格林”的评论

@polly 我已经发布 act-maven-archetypes 1.8.8.1 版了. 这个博客相关部分也已经够更新了
测试可行
罗格林
罗格林

引用来自“polly”的评论

Non-resolvable parent POM for demo.restful:simple-service:1.0-SNAPSHOT: Could not find artifact org.actframework:act-starter-parent:pom:1.8.8.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 13, column 11
@polly 我已经发布 act-maven-archetypes 1.8.8.1 版了. 这个博客相关部分也已经够更新了
罗格林
罗格林

引用来自“polly”的评论

Non-resolvable parent POM for demo.restful:simple-service:1.0-SNAPSHOT: Could not find artifact org.actframework:act-starter-parent:pom:1.8.8.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 13, column 11
真是不好意思, 请打开 pom.xml 文件, 删掉 parent 里面的 `-SNAPSHOT` 再试试. 我需要重新发布 archetype

非常非常感谢你, 你是第一个 follow 这篇博客的!

polly
polly
Non-resolvable parent POM for demo.restful:simple-service:1.0-SNAPSHOT: Could not find artifact org.actframework:act-starter-parent:pom:1.8.8.0-SNAPSHOT and 'parent.relativePath' points at wrong local POM @ line 13, column 11
青花瓷
青花瓷
mark 666
im1024
im1024
666
梧桐宇宇
梧桐宇宇
赞一个
wen66
wen66
act系列, 精品
Act-starter 1.8.8.0 强势发布 - 带来端到端自动化测试支持

Act-starter 1.8.8.0 是以 act-1.8.8-RC4 为核心的多个 starters 项目集合. 这个版本带来超过 40 项修改或增强: [act-e2e] 新的插件带来端到端自动化测试支持 [act-ebean]注册全局映射过滤器...

罗格林
05/14
0
0
JAX-RS服务端代码脚手架--srb4j

srb4j, Simple RESTFul backend for Java, 一个JAX-RS服务端代码脚手架(就像AppFuse之于Webapp),基于OAuth2提供了完整的注册、登录、密码恢复等功能。 它本质是一个Maven Archetype, 所以...

chenjianjx
2016/01/25
2.5K
4
极简的Restful框架推荐->Resty(服务端+客户端)

源码链接:OSC -> Resty Github -> Resty 在线开发手册 如果你还不是很了解restful,或者认为restful只是一种规范不具有实际意义,推荐一篇osc两年前的文章:RESTful API 设计最佳实践 和 In...

Dreampie
2015/01/08
0
67
极简的RESTful框架(服务端+客户端)--Resty

源码链接:OSC -> Resty Github -> Resty 在线开发手册 如果你还不是很了解restful,或者认为restful只是一种规范不具有实际意义,推荐一篇osc两年前的文章:RESTful API 设计最佳实践 和 In...

Dreampie
2015/01/04
18.9K
20
SpringCloud 自动生成API文档

在做项目的时候,如果项目是前后分离的,后端一定要和前端或者是移动端对接接口,那么问题来了,接口是不是要自己写给他们看,一般的会采用Excel或者Word来写,高级一点的就采用API管理平台手...

dounine
09/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

shell学习之创建函数

函数是一个脚本代码块,你可以为其命名并在代码中任何位置重用。 在bash shell脚本中创建函数基本使用如下两种方式,第二种格式更接近于其他编程语言中定义函数的方式。 function name {com...

woshixin
16分钟前
1
0
Toolfk.com 程序员工具网上线

#前言 真不容易,用了一个月的业余时间才把这个网站(toolfk.com)做完。里面的内容主要是从github上找的开源码码,在线运行使用的是Docker运行。当然也是现成的。因为我现在对Docker还是不懂。...

hihubs
30分钟前
2
0
svn 常用操作命令及问题处理

1. 常用命令 1.1. 基本使用 检出 checkout ➜ svn co repo_url -m '检出代码'➜ svn co repo_url saved_dir_name -m '检出代码,并指定目录名' 加入版本控制 add # 添加指定文件➜ svn...

whoru
57分钟前
3
0
记一次jquery validate的扩展(第一次失去焦点时触发校验)

最近在用jquery.validate 做前端表单校验,但是发现每次第一次失去焦点时,如果文本框内容为空,且该字段是必填项, 则不会触发校验,直到提交表单后,再次失去焦点时,才会触发,想对此进行...

foreach
今天
1
0
java生成UUID

UUID介绍: UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以...

编程SHA
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部