Moco is an easy setup stub framework, mainly focusing on testing and integration, inspired by Mock framework, e.g. Mockito, and Playframework
Integration, especially based on HTTP protocol, e.g. web service, REST etc, is wildly used in most our development.
In the old days, we just deployed another WAR to an application server, e.g. Jetty or Tomcat etc. As we all konw, it's so boring to develop a WAR and deploy it to any application server, even if we use embeded server. And the WAR needs to be reassembled even if we just want to change a little bit.
Moco has been published on Maven repository, so you can refer it directly in your code.
A gradle example is as follow:
repositories {
maven {
url "https://oss.sonatype.org/content/groups/public"
}
mavenCentral()
}
dependencies {
compile(
"com.github.dreamhead:moco-core:0.6-SNAPSHOT",
"com.github.dreamhead:moco-runner:0.6-SNAPSHOT"
)
}
Moco can be used as standalone to run with configuration and you can download standalone directly: Standalone Moco Runner
You have two ways to use Moco. One is API, which you can use in your unit test. The other is that run Moco as standalone. Currently, you put all your configuration in JSON file.
Here is an typical Moco test case in JUnit.
@Test
public void should_response_as_expected() {
MocoHttpServer server = httpserver(9090);
server.reponse("foo");
running(server, new Runnable() {
@Override
public void run() {
try {
Content content = Request.Get("http://localhost:9090").execute().returnContent();
assertThat(content.asString(), is("foo"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
As shown above, we created a new server and configure it as expected. And then run our test against this server.
Here, We use Apache Http Client Fluent API to request our testing server.
Now we are going to run Moco as standalone server. First of all, a JSON configuration file needs to be provided to start Moco.
[
{
"response" :
{
"text" : "foo"
}
}
]
It's time to run Moco standalone server:
java -jar moco-runner-<version>-standalone.jar -p 9090 foo.json
Now, open your browser and input "http://localhost:9090". You will see "foo". That's it.
Moco mainly focuses on server configuration. There are only two kinds of configuration right now: Request and Response.
That means if we get the expected request and then return our response. You have seen the simplest test case in previous example, that is, no matter what request is, return "foo" as response.
Now, you can see a Moco reference in details.
WARNING the json configuration below is just configuration snippet for one pair of request and response, instead of the whole configuration file.
If you want to response according to request content, Moco server can be configured as following:
- API
server.request(by("foo")).response("bar");
- JSON
{
"request" :
{
"text" : "foo"
},
"response" :
{
"text" : "bar"
}
}
If request content is too large, you can put it in a file:
- API
server.request(by(file("foo.request"))).response("bar");
- JSON
{
"request" :
{
"file" : "foo.request"
},
"response" :
{
"text" : "bar"
}
}
If request uri is your major focus, Moco server could be like this:
- API
server.request(by(uri("/foo"))).response("bar");
- JSON
{
"request" :
{
"uri" : "/foo"
},
"response" :
{
"text" : "bar"
}
}
Sometimes, your request has parameters:
- API
server.request(and(by(uri("/foo")), eq(query("param"), "blah"))).response("bar")
- JSON
{
"request" :
{
"uri" : "/foo"
"queries" : {
"param" : "blah"
}
},
"response" :
{
"text" : "bar"
}
}
It's easy to response based on specified HTTP method:
- API
server.get(by(uri("/foo"))).response("bar");
- JSON
{
"request" :
{
"method" : "get",
"uri" : "/foo"
},
"response" :
{
"text" : "bar"
}
}
Also for POST method:
- API
server.post(by("foo")).response("bar");
- JSON
{
"request" :
{
"method" : "post",
"text" : "foo"
},
"response" :
{
"text" : "bar"
}
}
We will focus HTTP header at times:
- API
server.request(eq(header("foo"), "bar")).response("blah")
- JSON
{
"request" :
{
"method" : "post",
"headers" : {
"content-type" : "application/json"
}
},
"response" :
{
"text" : "bar"
}
}
XML/HTML is popular format for HTTP server. Moco allows us to match request with XPath.
- API
server.request(eq(xpath("/request/parameters/id/text()"), "1")).response("bar");
- JSON
{
"request" :
{
"method" : "post",
"xpaths" : {
"/request/parameters/id/text()" : "1"
}
},
"response" :
{
"text" : "bar"
}
}
As you have seen in previous example, response with content is pretty easy.
- API
server.request(by("foo")).response("bar");
- JSON
{
"request" :
{
"text" : "foo"
},
"response" :
{
"text" : "bar"
}
}
The same as request, you can response with a file if content is too large to put in a string.
- API
server.request(by("foo")).response(file("bar.response"));
- JSON
{
"request" :
{
"text" : "foo"
},
"response" :
{
"file" : "bar.response"
}
}
Moco also supports HTTP status codein response.
- API
server.request(by("foo")).response(status(200));
- JSON
{
"request" :
{
"text" : "foo"
},
"response" :
{
"status" : 200
}
}
We can also specify HTTP header in response.
- API
server.request(by("foo")).response(header("content-type", "application/json"));
- JSON
{
"request" :
{
"text" : "foo"
},
"response" :
{
"headers" : {
"content-type" : "application/json"
}
}
}
We can also response with the specified url, just like a proxy.
- API
server.request(by("foo")).response(url("http://www.github.com"));
- JSON
{
"request" :
{
"text" : "foo"
},
"response" :
{
"url" : "http://www.github.com"
}
}
Redirect is a common case for normal web development. We can simply redirect a request to different url.
- API
server.get(by(uri("/redirect"))).redirectTo("http://www.github.com");
- JSON
[
{
"request" :
{
"uri" : "/redirect"
},
"redirectTo" : "http://www.github.com"
}
]
Sometimes, we need a latency to simulate slow server side operation.
- API
server.request(by("foo")).response(latency(5000));
- JSON
[
{
"request" :
{
"text" : "foo"
},
"response" :
{
"latency" : 5000
}
}
]
Sometimes, we want to simulate a real-world operation which change server side resource. For example:
- First time you request a resource and "foo" is returned
- We update this resource
- Again request the same URL, updated content, e.g. "bar" is expected.
We can do that by
server.request(by(uri("/foo"))).response(seq("foo", "bar", "blah"));
Moco allows us to mount a directory to uri.
- API
server.mount(dir, to("/uri"));
- JSON
{
"mount" :
{
"dir" : "dir",
"uri" : "/uri"
}
}
Wildcard is acceptable to filter specified files, e.g we can include by
- API
server.mount(dir, to("/uri"), include("*.txt"));
- JSON
{
"mount" :
{
"dir" : "dir",
"uri" : "/uri"
"includes" :
[
"*.txt"
]
}
}
or exclude by
- API
server.mount(dir, to("/uri"), exclude("*.txt"));
- JSON
{
"mount" :
{
"dir" : "dir",
"uri" : "/uri"
"excludes" :
[
"*.txt"
]
}
}
even compose them by
- API
server.mount(dir, to("/uri"), include("a.txt"), exclude("b.txt"), include("c.txt"));
- JSON
{
"mount" :
{
"dir" : "dir",
"uri" : "/uri"
"includes" :
[
"a.txt",
"b.txt"
],
"excludes" :
[
"c.txt"
]
}
}
Moco supports cache, which means you can cache resource access, so no need to access resource many times.
- API
server.response(cache(file("target.txt")));
- JSON
{
"response" :
{
"cache" :
{
"file" : "cache.response"
}
}
}
You can even persist your cache
- API
server.response(cache(file("target.txt"), with(file("persist.txt"))));
- JSON
{
"response" :
{
"cache" :
{
"file" : "cache.response",
"with" :
{
"file" : "src/test/resources/cache/cache.persistence"
}
}
}
}
TIPS even if the resource is not accessible, response can still be returned from persistence file.