I really try to like Node.js (Part 1)

I like the idea behind using Node.js for webapp development. I really do. The idea of using a consistent language between both client and server, which therefore means that you are able to share code between the two without replication. Who wouldn’t like that?

The problem is, I’m spoilt. I’m a Java Enterprise developer by trade, so I come into personal projects with a lot of expectations, simply because it’s what I would want to do at work. I try my best to relax these, but sometimes it’s just going too far. Specifically, a subset of the expectations that I want when doing my projects would be:

  • Good test support
    • Including Unit tests, Integration tests and Verification tests
  • Code coverage
  • Static analysis
    • To an extent, this includes compile time checks
  • Documentation support

Now, a lot of this you can get in Node. Of course, me being me I then further complicate things and make it hard to get some of these. (I’ve taken a real liking to ES6 instead of ES5, but because ES6 is best supported by Transpiling with something like Babel or Traceur, it means that the code that you run isn’t the code that you write. That in turn makes code coverage essentially pointless.)

Documentation of your code in Node is easy enough. There’s a fair few projects that will cover this for you, including the one that I’ve been using - esdoc - that supports ES6 constructs. To be fair, any language that didn’t allow you to document your code would be a real non-starter.

Static analysis of Javascript has a few different alternatives. On the one hand, with ES5 code you have tools like jshint. On the other hand, because I’ve been working with ES6 code then the transpiler does a level of this automatically so that’s not a problem.

Code coverage is possible in Node, but as I say when working with ES6 code it’s relatively pointless. You want to know how much of the code that you wrote was tested, not how much of the code that was generated by the transpiler. However, for people who want to do this there are tools like blanket and instanbul.

And of course, Node has a large number of libraries for testing your code. I’ve been a fan of Mocha + Chai, but there are plenty of others to choose from as well, both for the actual test harness and for the assertions. That’s not the problem at all.

This is where my problems start. When you write Unit Tests - as opposed to Integration Tests - then the idea is that you test one single Unit in isolation. This typically means that whenever your unit has external dependencies then you replace them with something that is under your control. This something is a Mock or a Stub. Now, there are ways of doing this in Node. In particular, I’ve been using Sinon as a Mocking library because it actually gives very good support for creating mocks and stubs, as well as what it calls spies, and lets you confirm that you module works as expected.

The rub is getting these mocks into your code. The ES6 module system encourages you to have modules that include public (i.e. exported) and private components, and that depend directly on the public parts of other modules. There’s no need for any kind of Dependency Injection system with all of this, because you just depend on the modules in question and tie things together that way. And for writing the code itself, that works really well. The problem is that for testing your code this is a complete nightmare. Working this way, you would need some way of overriding the dependencies that are pulled in to your modules after they have loaded. Now, there are modules for traditional ES5 Node that allow you to do this - e.g. Rewire - but they all feel a bit hacky, and they fall down quite badly when you start trying to do ES6 node instead. Rewire requires that you use it instead of the built in ‘require’ statement, which in ES6 you don’t use (At least not directly. Babel re-writes the import statements to be require statements for you). Mockery, on the other hand, breaks in on the Node module loading system, but you need to have set it up before the appropriate require calls are made, which you can’t do in ES6 since the import statements must be top level.

And so, you end up with lots of units of code that you just don’t bother to test, because you can’t be bothered to work out how to get around this problem. And that in turn means that your code is a bit brittle to change, because you don’t have the coverage necessary to know that things still work when you change it. And then that leads to code that you’re scared of changing, which means that it ends up just as a mess. My latest project is only 17 files large - I’ve barely started - and it’s already feeling messy and out of control because of this.

And that sucks, because on the face of it I like Node.