Reduce Client Side Testing Time with MSL

Author: Daniel Koo


Introduction

Mock Service Layer (MSL) allows you to provide client side integration testing. Instead of end to end browser testing and waiting on various dependencies, MSL allows faster front end testing by mocking the service layer with a hermetic server. At FINRA, it’s dramatically cut down our testing time. End to end full application tests once took us 16 hours. With MSL, our testing time has now dropped to 3 hours.

You can run this with various clients including Browsers, Python, Ruby, and Java. Here’s how to get started.

Starting MSL Server

To begin, you need to install node. You can go to nodejs.org for more information. Once you installed node, you can install msl-server using npm:
Local Install
#Install msl-server
npm install msl-server

#Run msl-server
./node_modules/msl-server/bin/msl [options]


Global Install
#Install msl-server
npm install -g msl-server

#Run msl-server
msl [options]

Following are the options you can include as execution parameters:

--basedir => Specifies the root directory (absolute path) of the app you want to launch locally. The default is the directory where you run the command.
--port => Specifies the port that server will be listening on local host. The default is 8000. --debug => Specifies whether to output log in console or not. The default is false.

Mocking HTTP Requests

Now that you have msl-server up and running, you can use one of our MSL clients to interact with the server to setup mocks. This is especially useful when you want to rapidly run ad-hoc tests for your front-end code during development. There are currently 5 clients available:

  • Browser
  • Node
  • Java
  • Python
  • Ruby

In this blog post, I will be showing how to use the node client (go here to view other options)

Installing Node Client
npm install msl-client

To stage mocks, you can use the node interactive mode to execute arbitrary JavaScript or create a JavaScript file for node to execute. Interactive mode is useful when you want to stage mocks one at a time while manually testing your application. A JavaScript file is useful when you want to stage multiple mocks and test your application. The following example will show how to stage multiple mocks via JavaScript file.

mocks.js

// require msl client
var msl = require('msl-client');

// Set up mock 1
var configurations = {};
configurations.requestPath = "/services/getFirmNames";
configurations.responseText = '{"firmName":"First Firm Name"},{"firmName":"Second Firm Name"}';
configurations.contentType = "application/json";
configurations.statusCode = 200;

// Stage mock 1 on msl server
msl.setMockRespond("localhost", 8001, configurations);

// Set up mock 2
configurations.requestPath = "/services/getFirmLocations";
configurations.responseText = '{"location":"Rockville"},{"location":"New York"},{"location":"Washington, DC"}';
configurations.contentType = "application/json";
configurations.statusCode = 200;

// Stage mock 2 on msl server
msl.setMockRespond("localhost", 8001, configurations);

By default, contentType is "application/json" and the statusCode is 200 so you may choose to omit setting up those configs. There are other configurations as well as default values that you can use when you stage your mocks. You can find more details here.

Once you create your JavaScript file, you can execute the following to have the mocks staged on MSL Server.


Stage Mocks
node mocks.js

When you execute the above, you will be able to see on the msl-server console that mocks have been registered (if you have the debug turned on when starting up msl-server). Then you can open up your application via the browser and perform actions that will trigger /services/getFirmNames or /services/getFirmLocations requests. When those actions are triggered, msl-server will respond with the mocks that you have staged using mocks.js instead of hitting the actual service.

This approach will allow you to test your front-end code without running the entire stack. You can also develop and test your front-end code in isolation even before the services are implemented.

Running Tests using Karma

Karma is a popular testing framework for running JavaScript unit tests. MSL project has a karma plugin which allows MSL Server to be launched and use the MSL browser client. Here’s an example of how to use karma-msl plugin.

Install karma-msl as dev dependency
{
"devDependencies": {
"karma": "~0.12.0",
"karma-msl": "~0.0.16"
}
}


Install karma-msl locally
npm install karma-msl --save-dev

After installing karma-msl, you can configure your karma config as followed.

karma.conf.js
module.exports = function(config) {
config.set({
frameworks: ['jasmine', 'msl'],

files: [
'sample-app/test/spec/sampleSpec.js' //your test spec file
],

// configuration for msl plugin
msl: {
port: '8002', //port to start msl server. 8000 by default.
basedir: 'sample-app/dist', // directory containing the app code (front-end code under test). current dir by default.
},
// this port should match the msl port specified in msl plugin config
proxies: {
'/' : 'http://localhost:8002/'
},

// this port should match the msl port used within the test spec files
port: 8001,

... Here you should include other standard karma configs such as browser, reporting, plugins, etc
});
};

Here's an example test spec file.
sampleSpec.js
describe('Example test suite', function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

beforeEach(function(done) {
// Load app under test
openApp('http://localhost:8001/index.html');

setTimeout(function() {
done();
}, 200);
});

afterEach(function() {
unRegisterMock('localhost', 8001, "");
});

it('Test drop down values loaded correct', function(done) {
// Use msl-client to set mock response
var configurations = {};
configurations.requestPath = "/services/getFirmNames";
configurations.responseText = '{"firmName":"First Firm Name"},{"firmName":"Second Firm Name"}';
configurations.contentType = "application/json";
configurations.statusCode = 200;
setMockRespond('localhost', 8001, configurations);

// Type into first input field which triggers a REST call to return a JSON response
getElement('#autocomplete').val('F');
getElement('#autocomplete').keydown();

setTimeout(function() {
// Validate the drop down is display correctly with mock response
expect(getElement('ul li:nth-of-type(1)').text()).toBe('First Firm Name');
expect(getElement('ul li:nth-of-type(2)').text()).toBe('Second Firm Name');

// Click on the Second item from the drop down
getElement('.ui-autocomplete .ui-menu-item:nth-of-type(2)').click();

// Validate that correct item was selected
expect(getElement('#autocomplete').val()).toBe('Second Firm Name');

done();
}, 500);
});


it('Test XHR intercept on post method call', function(done) {
// Use msl-client to register intercept
setInterceptXHR('localhost', 8001, '/services/postService');

// Type into second input field and click GET button which triggers a GET request
getElement('#name').val('Sample Name');
getElement('#submit').click();

setTimeout(function() {
// Retrieve intercepted XHR and validate correct POST request was made by the app
getInterceptedXHR('localhost', 8001, '/services/postService', function(resp) {
var intrReq = JSON.parse(resp).xhr_1;
expect(intrReq.xhr.url).toBe('/services/postService');
expect(intrReq.post['text']).toBe('Sample Name');
expect(intrReq.xhr.method).toBe('POST');
});

done();
}, 500);
});
}

Run karma tests
karma start karma.conf.js

Conclusion

MSL can help reduce your testing time and help you move toward more agile continuous development. Want more detailed information? Visit the MSL homepage for more information including user docs.