接口测试

前段时间对系统中所有的接口Web Service添加了测试用例。这些用例在执行的的过程中多少也会受依赖环境的影响。比如RestAssured:

public class RestInterfaceIT {@Beforepublic void setUp() throws Exception {RestAssured.baseURI = "http://host:port";}@Testpublic void should_get_false_given_header_and_param() {String selfHeader = "hello";given().header("selfHeader", selfHeader).get("/xxxxx/yyyyy/{param}", "value").then().statusCode(200).body("jobStatusResult.sent", is("false"));}
}

通过给Restful传递参数,判断返回值来断言接口测试。

在编写过程中发现:
1. 每次本地编写接口的测试用例时,都会依赖接口的环境。多多少少也对进度有点影响。
2. 编写每个用例时有部分时间竟然花费在找合适的测试数据上。
3. 当某个接口要升级接口时,还要等待提供方先将升级部署到测试环境。

后来跟Thoughtworks的 coach 取经,对方推荐了Moco 框架。体验后,不得不说,对于前后端开发、接口开发,这是个神器的框架。

Moco

简单来说Moco就是类似一个mock的工具框架。在Moco 的github上面有这段话。

Integration, especially based on HTTP protocol, e.g. web service, REST etc, is wildly used in most of our development.
In the old days, we just deployed another WAR to an application server, e.g. Jetty or Tomcat etc. As we all know, it's so boring to develop a WAR and deploy it to any application server, even if we use an embeded server. And the WAR needs to be reassembled even if we just want to change a little bit.

翻译:
集成,特别是基于HTTP协议的集成,例如web服务、REST等,在我们的大多数开发中都被广泛使用。

在过去,我们只是将另一场战争部署到应用服务器上,例如Jetty或Tomcat等。众所周知,开发一个WAR包并将其部署到任何应用服务器上是非常枯燥的,即使我们使用的是嵌入式服务器。war包也需要被重新打包即使我们只是想稍微改变一下。

简单来说,Moco就是解决了开发前端时没有后端支持,开发接口时依赖没有到位的尴尬场景。当然Moco的灵活性,让其有越来越多的应用场景。当然目前也有一些轻量级的服务器供选择,如HTTP使用的Jetty、Restlet、Roo、Netty和Play等。但个人感觉还是Moco好用:

1.只需要简单的配置request、response等即可满足要求,支持http、https、socket。可以说是非常的灵活性。
2.支持在request 中设置 Headers , Cookies , StatusCode等。
3.对GET、POST、PUT、DELETE等请求方式均支持,很适合web开发。
4.无需环境配置,有java环境即可。
5.修改配置后,立刻生效。只需要维护接口,也就是契约即可。
6.对可能用到的数据格式都支持,如json、text、xml、file等。
7.还能与其他工具集成,如Junit、Maven、Gradle等。

安装

Moco的使用非常简单,从官网上下载一个jar包。然后相同目录下创建一个json配置文件即可(例子来自于官网):

[{"response" :{"text" : "Hello, Moco"}}
]

注意,这里
请求配置文件是个数组,也就是说,可以在一个文件中配置多个接口的请求和响应。下载jar包后,在当前目录运行以下命令:

java -jar moco-runner-0.12.0-standalone.jar http -p 12306 -c foo.json

注意:-p 是指定12306 端口作为服务使用。

然后在浏览器中输入: http://localhost:12306 即可看到响应。

整个使用步骤与Stubby4j ,Mountebank 很类似。

Moco使用

Moco通过简单的配置request和response 对象,达到模拟请求效果。

Param和Header

Moco也可以模拟一个请求,同时附加参数。另外也支持在Http header里面自定义头部。例如在json配置里面:

{"request":{"uri": "/hust","queries":{"param": "zw"}},"response":{"text": "Hello, Moco","headers":{"SelfHeader":"SelfHeader"}}
}

此时在浏览器中输入:http://localhost:12306/hust?param=zw

即可看到结果。查看请求头部。

Moco入门-编程知识网

重定向

Moco也可以帮我们模拟重定向,这点可以模拟一些请求拦截、请求验证失败等情况。

{"request":{"uri": "/redirect"},"redirectTo": "http://www.baidu.com"
}

json和file

上边的例子中,我们设置的都是返回文本 text。Moco也支持返回json和file数据。

{"request":{"uri": "/json"},"response":{"json":{"name": "hustzw"}}
},
{"request":{"uri": "/file"},"response":{"file":"data.json"}
}

Https

Moco能模拟Http的请求,也支持Https请求。我们知道Https是需要证书cert的验证。一般这个要像证书中心(CA)申请的,而且是要收费的。不过一些小的公司或者开发中可以自己生成。Java环境自带的keytool 工具就能帮我们生成这个证书jks。

Keytool

Keytool 是一个JAVA环境下的安全钥匙与证书的管理工具,位于%JAVA_HOME%\bin\keytool.exe。Keytool将密钥(key)和证书(certificates)存在一个称为keystore 的文件(受密码保护)中。可以用它生成密钥库文件。

keytool 命令如下:

Moco入门-编程知识网

参考上边命令,执行以下命令就可以生成我们的密钥了。

keytool -genkey -keyalg RSA -keysize 1024 -validity 365 -dname "CN=hustzw, OU=hust,O=zw, L=zhuhai, ST=guangdong, C=CN" -alias my_key -keypass 123456 -keystore my.jks -storepass 123456

之后你就能在当前目录下看到密钥库文件,my.jsk了。

然后在启动Moco时指定密钥文件即可。

java -jar moco-runner-0.12.0-standalone.jar https -p 12306 -c foo.json --https my.jks --cert 123456 --keystore 123456

此时打开浏览器输入:https://localhost:12306/json

可以看到页面变成下面这样。然后添加信任改页面即可。

Moco入门-编程知识网

Junit

Moco 是一个搭建模拟服务器的工具,其支持 API 和独立运行两种方式。上边我们都是通过一个jar包开启服务,模拟一个后台请求服务器的。其实Moco也提供API的用法,且能很好的与Junit和Maven等集成。

看下官网的例子,先为项目添加以下依赖:

	<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version></dependency><dependency><groupId>com.github.dreamhead</groupId><artifactId>moco-core</artifactId><version>0.12.0</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>fluent-hc</artifactId><version>4.5</version></dependency><!--other dependency--></dependencies>

这里利用了
fluent-hc 的jar做客户端请求。

下面这个是github的例子:

package com.oocl;import com.github.dreamhead.moco.HttpServer;
import com.github.dreamhead.moco.Runnable;
import org.apache.http.client.fluent.Content;
import org.apache.http.client.fluent.Request;
import org.junit.Test;import java.io.IOException;import static com.github.dreamhead.moco.Moco.httpServer;
import static com.github.dreamhead.moco.Runner.running;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
public class TestMoco {@Testpublic void should_response_as_expected() throws Exception {// prepare a mock serverHttpServer server = httpServer(12306);server.response("foo");running(server, new Runnable() {public void run() throws IOException {Content content = Request.Get("http://localhost:12306").execute().returnContent();assertThat(content.asString(), is("foo"));}});}
}

步骤很明了,先利用Moco准备一个Server,设置它的返回值。然后验证请求返回的Content。

而实际应用中,我们会有大量请求。这样会让测试代码变多。因此还是回到上边的foo.json。这里要引入新的jar包。

		<!-- https://mvnrepository.com/artifact/com.github.dreamhead/moco-junit --><dependency><groupId>com.github.dreamhead</groupId><artifactId>moco-junit</artifactId><version>0.12.0</version></dependency><!-- https://mvnrepository.com/artifact/com.github.dreamhead/moco-runner --><dependency><groupId>com.github.dreamhead</groupId><artifactId>moco-runner</artifactId><version>0.12.0</version></dependency>

这样我们就能通过runner加载foo.json的配置。然后只需要编写测试代码即可。

import com.github.dreamhead.moco.Moco;
import com.github.dreamhead.moco.junit.MocoJunitRunner;
import org.apache.http.HttpResponse;
import org.apache.http.client.fluent.Content;
import org.apache.http.client.fluent.Request;
import org.junit.Rule;
import org.junit.Test;import java.io.IOException;import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;public class MocoTest {@Rulepublic MocoJunitRunner runner = MocoJunitRunner.jsonHttpRunner(12306, Moco.pathResource("foo.json"));@Testpublic void testNoUri() throws IOException {Content content = Request.Get("http://localhost:12306").execute().returnContent();assertThat(content.asString(), is("Hello, Moco"));}@Testpublic void testWithParamAndHeader() throws IOException {HttpResponse httpResponse = Request.Get("http://localhost:12306/hust?param=zw").execute().returnResponse();assertThat(httpResponse.getFirstHeader("SelfHeader").getValue(), is("SelfHeader"));}
}

目录结构如下:

Moco入门-编程知识网

MocoJunitRunner加载resources下的配置文件,然后自动构建一个mock的服务器。更多用法可以参考文档。

所有代码