Fun with Stamps. Episode 17. Easy 100% unit test coverage in JS

Vasyl Boroviak
6 min readJul 23, 2017

Hello. I’m developer Vasyl Boroviak and welcome to the seventeenth episode of Vasyl Boroviak presents Fun with Stamps.

TL;DR

Using this simple technique you can cover basically any JavaScript code with unit test. You would not need proxyquire or any other module mocking. You would not need messing around with setTimeout or any other builtin JavaScript API. The code and the tests will still be easy to read and maintain.

And, of course, we will utilise stamps to make it even easier. The main part — unit tests — are in the middle of this article.

All the source code can be found here: https://github.com/koresar/fws-ep17

Let’s write a node.js app

The node.js application code below is rather complicated to quality cover it with unit tests. It was chosen to show you how even the most complex code can be covered in JavaScript using stamps.

A production ready node.js service typically looks like this. I’m using the express-generator CLI to generate our app.js.

As you can see there is already a lot of dependencies which might cause troubles while writing unit (not integration!) tests for that file:

  • External modules express, debug, jade, etc.
  • Built in modules path and http.
  • Built in global variables require, process, __dirname and setImmediate.
  • I/O, such as sockets and file system.

Your task

You should make sure:

  • no one ever share static files other than those inside the ./public directory;
  • no one ever removes that extended: false because it’s a security concern of this service;
  • we never return stack trace inside an error response payload. Again, because security matters to us;
  • no one ever removes that setImmediate because it’s a data consistency concern.

Yes, we are going to have all that listed above. But additionally:

  • we won’t depend on sinon or proxyquire etc;
  • we will do only few small changes to the code
  • thus, the code will still be readable
  • and easy to change and maintain as it is now.

To have that all we will use the simple technique called early dependency injection utilising stamps.

The main and only refactoring

To create our app2.js I’m going to add this. to few variables. So, instead of keeping them in the function scope we will save them to an object. Wait saying “Booo!” You have to see how the unit tests look after the refactoring. It’s important!

Take a look at how we prepared our code for the dependency injection. As you can see we pushed all the problematic dependencies to the this. object using stamp’s props. And we moved the two route handlers to the stamp’s methods down below.

Also, the module does not export a function any more, but a stamp. The stamp will start the service when an object instance is created from it.

So, now we can change dependencies just before starting the service. Like so:

let App = require('./app')
App = App.props({
http: myMockedHttpModule
})
App() // start the service

In the code above I replaced the actual http module with something like a mock object:

let myMockedHttpModule = {
createServer() {
return {
on() {},
listen() {}
};
}
};

Now you can test the “Application” and the “Routing” parts of the app (see the comments separating the file onto three sections) without opening any sockets! Just use that mocked App from the code above in all your tests.

Unit tests

“Application” part of the service

  • Making sure we share nothing but the ./public folder.
  • Making sure the extended is always false in bodyParser.urlencoded().

At the top of this file we are creating an http dependency which does not open any ports. Instead of the actual .listen() method we put an empty function.

As you can see we are setting the http dependency only once (that’s called the early dependency injection) and then transparently reusing it in the all the tests of this file. So that the it() tests look clean.

Look closer at the tests. We mock only the only relevant dependencyexpress or bodyParser. This hugely improves readability!

The first test makes sure that we do actually share the ./public directory.

The second test makes sure that any time we share a directory we actually share the /public folder(s). No matter how many times the .static() function is called.

The third test makes sure that urlencoded({extended}) option is false.

It is hard to over stress the point that the tests are not bloated, that they are simple, and anyone with the basic JavaScript knowledge can read and amend them as needed.

“Routing” part of the service

  • Making sure the error stack trace is not exposed when running in “production” environment.

If you read this 5 minute article — Episode 1. Stamp basics — you will find that every stamp has the .compose property. It’s a function which also serves as the metadata holder (aka “stamp descriptor”).

Philosophy of stamps: you are allowed and encouraged to extract and manipulate a stamp’s metadata.

At the top we retrieve the two route handling methods from the metadata — handleNotFound and handleGenericError. And then we simply unit tests them as regular functions.

That’s how you can extract each individual method (or property, or initializer, or static property, etc) and test it without creating an instance of that stamp.

“Server” part of the service

  • Making sure we exit the node.js process only after the setImmediate settles the I/O of the process.

This is the most complicated test. We mock three things: the http module, the setImmediate JavaScript global function, and the process global node.js variable.

The done() function is executed only after we made sure that the setImmediate and process.exit were actually called.

This test would have been simpler if we would move few other variables to the props of the stamp. However, my point here is to show you that a code of any complexity can be covered with simple unit tests; and that you don’t need any sophisticated NPM modules for mocking or dependency injection.

All you need is to try stamps.

More complex testing scenarios

Any future complications of the app2.js will still be easy to unit test.

For example, what if you would need to open a Postgres database connection before opening the HTTP port?

Easy! Just push it to the stamp’s props like we did with the http module dependency. (Try it by forking this repository.)

But, if you would attempt writing this unit test using the original app.js approach you’d need to put more effort mocking the database dependency.

Conclusion

It’s difficult to unlearn the typical way we do software development. Classes or pure functions are both great ways to code in JavaScript. Although, I do believe that by using stamps we can all be more productive as developers.

When I code, my typical .js files export a single thing — a stamp. There is a number of positives in this approach. I’ll list them in the order of importance.

  • Most important. Your unit tests are simple, readable, and maintainable. It’s hard to underestimate the importance of this in the long run.
  • Second. You don’t need proxyquire, nock, sinon, or similar mocking libraries. And the unit testing code is KISS (Keep It Stupid Simple) and DRY (Don’t Repeat Yourself) enough.
  • Third. After our small refactoring the original application code does not change much. The code readability stays the same.
  • And last. The tests above will run under any compatible testing framework (mocha, jasmine, jest). Try it! Also, if you replace describe and it with tape or ava syntax — the tests will still run.

Have fun with stamps!

--

--