May 04, 2016

Mocking Spring Microservices With Node.js and Gradle

Tim Waite
Trey Sedate

Tim Waite and Trey Sedate

Default image background

One of the great things about developing with a microservice architecture is it allows for a separation of concerns. This helps speed up development time, as each piece can be built independently, including the front end and back end. However, when building out a front-end piece we often don’t yet have the back-end service ready. In order to facilitate this type of development we need a way to mock the data calls.

Our current project uses Spring Cloud Netflix, including Eureka and Zuul with Spring Boot microservices, and although we were able to separate concerns between different back-end pieces, work on these services was not being completed until the end of the sprint. This left our front-end developers with only one or two days to hook into real data, discover edge cases, and resolve any defects surrounding misplaced assumptions about the data. We needed a way to mock any number of services while still allowing them to be routed through Zuul, the reverse proxy server, to best imitate the actual architecture.

In order to do this we created a node server and added options to Zuul’s build.gradle file to allow us to reconfigure Zuul’s route configuration with simple command line arguments.

Below is the code we used to create this mock server:

var express = require('express'); var url = require('url'); var app = express(); app.use(express.static(__dirname)); app.all('*',function(req,res) { setTimeout(function() { req.url = stripTrailingSlash(req.url); console.log(req.url); res.sendFile(__dirname + url.parse(req.url).pathname + '.json'); }, 300); }); app.listen(7000, function () { console.log('Mock server listening on port 7000!'); }); function stripTrailingSlash(str) { if(str.substr(-1) === '/') { return str.substr(0, str.length - 1); } return str; };

The above code simply serves files, where each file contains only a single json object and the location of the file corresponds to the endpoint being called, regardless of the request method. A sample file structure would look like this:


In this case, any request to the /service_test/another_route/post/ endpoint will return the file service_test/another_route/post.json.

Although we can easily configure Zuul to route a service to our node server instead of the real microservice, how can we easily reconfigure an arbitrary number of routes without having to manually change Zuul’s application.yml every time we want to mock a service?

The following adjustments to your bootRun task will allow you to use a -Pmock command line argument in order to route to the mocked version of any service you want.

bootRun { if (project.hasProperty("mock")) { def services = ["one", "two", "three", "four", "five", "six", "etc"] def toMock = project.mock.tokenize(',') for (service in toMock) { if (service == "all") { toMock = services } else if (service.startsWith("-")) { toMock = toMock.findAll { it != service.substring(1) && it != service } } } println "Mocking:" + toMock toMock.each { systemProperties['zuul.routes.service_' + it + '.url']='http://localhost:7000/service_' + it } } }

With the above code, you only need to specify the name of the service to be mocked, and Zuul’s route configuration is automatically changed to send requests to the mock server instead of the microservice. Because of Spring Boot’s PropertySource order for externalized configuration, the routes we add here take precedence over the ones included in application.yml. For example:

gradle bootRun -Pmock=all ← mock all services

gradle bootRun –Pmock=one,two,three ← mock services one, two, and three

gradle bootRun –Pmock=all,-two,-four  ← mock all services except two and four

By using a node server and adding a few lines to Zuul’s bootRun task, we can mock any microservice while still using Eureka and other unmocked microservices as normal. Zuul will use the route configuration you set up for any unmocked services, and the rest will be routed to and mocked by the node server.

Conversation Icon

Contact Us

Ready to achieve your vision? We're here to help.

We'd love to start a conversation. Fill out the form and we'll connect you with the right person.

Searching for a new career?

View job openings