In Depth on my first NodeJs / Express Stack

The whole goal of my hobby site is to iteratively create the simplest website that could possibly work using all “new to me” stacks. Getting a static site up and running on my own Linux CentOS with Lighttpd was definitely simple. Getting a NodeJs web service up and running with Express was equally simple, but making it production quality was definitely a frustrating experience for me.

My initial experience with NodeJs was exhilarating. I had a web server & app running with just a few lines of code, but then I quickly soured on the whole platform when I found TDD & mocking to be more difficult than I feel it should be. I’m happy to say that through a bit of research and effort, I now have a stack that I’m relatively happy with.  I want to capture the struggles and solutions while they’re still fresh in my mind.

Warning: This turned into a very long post. There’s a TL;DR at the bottom if you want to skip to the end. Also note that this is in no way meant to be tutorial. It’s more of a brain dump of my notes from the last few weeks of learning Node/Express. I may not be doing things in a good way or completely understand the tech I’m working with here, so take everything with it’s due grain of salt.

Most of my web experience is with Asp.Net MVC, which is a very opinionated framework. Node with Express, on the other hand, is not opinionated. I think this was ultimately the source of my frustration. Express is crazy flexible and absolutely unopinionated about how you structure your application and code. Having the freedom to architect my code base as I see fit is amazing, but having to cobble together your own stack is a daunting task when you’re new to the ecosystem. Jose Aguinaga’s recent post, How it feels to learn Javascript in 2016 comes to mind. I had to learn seven libraries (and threw away more than a few) to come up with a stack I feel good about.

The Goal:

Create a Todo service to manage the backlog for my site.

Getting Started

I chose Express for my web framework due to its apparent popularity and good documentation and I’ve got to say that I’m happy with the choice. As I mentioned earlier, it’s a very flexible framework. It has sensible defaults, is unopinionated, has a great plug-in system (along with a great number of community created plug-ins), and fantastic documentation. (If I’m hung up on the documentation, it’s because many of the projects I came across did not.)

npm install express --save

All of the code examples in the quick start guide do have you hard code your application’s behavior directly into the app’s route handlers though.

app.get('/', function (req, res) {
  res.send('Hello World!')
})

As soon as I got past “Hello World” I was searching for a way to get that logic separated from the actual routing. Creating a module and delegating to it was easy enough to accomplish.

./controllers/root.js

exports.index = function(request, response){
  response.send('Hello World!');
}

./index.js

var app = express();
var root = require('./controllers/root');
app.get('/', root.index);

app.listen(1337, function () {
  console.log('Web api is listening on port 1337');
});

Much better. Now the logic is in a place where I can properly test it without starting the server up.

Unit Testing Framework

The next thing to do was pick a unit testing framework. This was done more or less at random with a google search. I went with Mocha & Chai and I have zero regrets about this decision. They both work beautifully, and I have to admit that I’m surprised at how much I like the “describe it” paradigm of Mocha and Chai’s “expect(thing).to…” api. I come from an “Assert.Equals(expected, actual)” background and I had no idea what I was missing out on. In my opinion, the best part about Mocha is the ability to nest its “describes” functions. It enables a great way of grouping tests and fixture state together.

npm install mocha --save-dev

npm install chai --save-dev


describe('TodoController', function(){

    describe('findAll', function(){
        it('should return all items', function(){
	    ...
        });

        it('should return a 200 ok', function(){
            ...
        });
    });

    describe('findById', function(){

        describe('when item is found', function(){

            it('should return 200 ok', function(){
                ...
            });

            it('should only return the expected item', function(){
                ...
            });
        });

Giving you output like this.

  •  TodoController
    • findAll
      √ should return all items
      √ should return a 200 ok
    • findById
      • when item is found
        √ should return 200 ok
        √ should only return the expected item
      • when item is not found
        √ should return 404 not found

Gotchya #1: Be sure to place your tests in the ./test/ directory. Otherwise Mocha won’t find and run them. I initially put them under ./tests/ and pulled my hair out trying to figure out why they weren’t running.

Unit Testing Routes/Controllers

But I’m getting ahead of myself. There is a major barrier to test driving the code still. Remember this code?

exports.index = function(request, response){
  response.send('Hello World!');
}

It doesn’t return anything, so in order to assert that the expected response was sent, we’ll need to spy on the response argument a bit. I prefer to test outputs, but so be it. I caught wind of http mocks from Morris Singer’s presentation about testing with Mocha and installed express-mocks-http.

This was my first major hurdle and it took me way too long to figure out what was wrong. I couldn’t seem to get the mock to give me any data back. It turns out that the github project that express-mocks-http links to on npm is actually node-mocks-http. So, I installed express mocks, but was trying to use the documentation for node mocks… It took me several hours of cursing to realize that the project changed names and that express-mocks-http is (apparently) defunct. I needed to install node-mocks-http instead.

Gotchya #2: Don’t use express-mocks-http, use node-mocks-http. 

npm install node-mocks-http

Once I had the right version of the library, creating mocks for the request and response objects was dead dumb simple and very effective.

var mocha = require('mocha'),
    expect = require('chai').expect,
    httpMocks = require('node-mocks-http');

var root = require('../controllers/root');

describe('root controller', function(){
  var req, res;

  beforeEach(function(){
    req = httpMocks.createRequest();
    res = httpMocks.createResponse();
  });

  it('returns 200 ok', function(){
    root.index(req, res);
    expect(res.statusCode).to.equal(200);
  });

  it('says hello', function(){
    root.index(req, res);
    expect(res._getData()).to.equal('Hello World!');
  });
)};

Debugging Tests with Visual Studio Code

Gotchya #3: Running tests was easy, but how to debug a test?

Inevitably, we’re going to need to step into a test to see what’s going on, particularly when we’re learning a new language. Running tests with Mocha was easy. All I had to do was add this to my package.json and then run them with npm test.

"scripts": {
    "test": "mocha"
  }

Actually debugging the test took me a minute to figure out. I knew that I wanted to add a launch configuration that would run the tests, and stop at any breakpoints, but I wasn’t sure how to do that. A bit of searching led me to this comment on a gist. All I did was add the --colors argument to make the debug console print out in color instead of white.

        {
            "name": "Debug Tests",
            "type": "node",
            "request": "launch",
            "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
            "stopOnEntry": false,
            "args": ["test/**/*.js", "--no-timeouts", "--colors"],
            "cwd": "${workspaceRoot}",
            "runtimeExecutable": null,
            "env": { "NODE_ENV": "testing"}
        }

This gives me the same beautiful output that mocha gives us in the terminal and breakpoints/stepping work perfectly.

capture

Adding a database

At this point, I had some basic unit tests was returning some hardcoded data from my endpoints. It was time to pick a storage mechanism. I could have installed MySQL easily enough, but that seemed… heavy for what I wanted to do. I’m only storing a little bit of data related to a single entity. Could I do something simpler?

Of course I could. Just write the data to a file!

I’m really happy I decided to go that route, because when I searched for “lightweight node file database” I found LokiJs. It’s a lightweight, in memory, json database for Node. Its API is a subset of MongoDb’s, so if you want to switch over down the road, you can. I suppose this is all NoSQL, which is cool I guess. I wished I had understood earlier that NoSQL meant “store/retrieve a file from the file system and query for parts of it” sooner. I’m sure that a gross under statement  of what NoSQL actually is, but whatever. Loki perfectly met my needs for saving and retrieving some json data.

npm install lokijs --save

So, the first thing you probably want to do is fire up node at the command prompt and create a database and some dummy data to work with.

node
> var loki = require('lokijs');
> var db = new loki('test.json');
> var todo = db.addCollection('todo');
> todo.insert({title:'Finish this blog'});
> db.save();

This will place a test.json file in your current working directory that looks something like this. This is our database.

{
  "filename":"test.json",
  "collections":[{"name":"todo",
  "data":[{
    "title":"Finish this blog",
    "meta":{"revision":0,"created":1480501898872,"version":0},
    "$loki":1}],
  "idIndex":[1],
  "binaryIndices":{},
  "constraints":null,
//.....

Retrieving all the items in the collection is a simple affair… once you understand just how completely asynchronous javascript really is. My first attempt to read this data back out of Loki consistently returned nothing.

var loki = require('lokijs'),
  db = new loki('todo.json');

exports.findAll = function(request, response) {

  db.loadDatabase();
  var data = db.getCollection('todo').data;
  response.send(data);
}

Gotchya #4: Javascript is ruthlessly asynchronous. Just because it works in the terminal doesn’t mean it will work the same in your code.

If you’re familiar with Javascript, you’re probably laughing at me right now. I ran the commands above in the Node terminal and everything worked perfectly. I placed it into a code file and data was consistently nothing. It took me an absurd amount of time to figure out why.  If I had just checked the documentation earlier, I probably would have saved myself that time and a headache to boot. This is what the code should look like.

var loki = require('lokijs'),
  db = new loki('todo.json');

exports.findAll = function(request, response) {

  db.loadDatabase({}, function(){
  var data = db.getCollection('todo').data;
  response.send(data);
 });
}

It passes an anonymous callback into the loadDatabase() method that does the actual work. Apparently when I executed the command in the terminal, it had enough time to completely load the database (or the terminal forced completion, I’m not sure which) before I executed the next command. When running from a file, it just fires off the commands as soon as it comes across them, leaving me with 2 tons of confusion. The lessons learned here are two fold.

  1. Read the frakkin’ manual.
  2. Callbacks are everywhere.

Buzz LightYear and Woody. Callbacks Everywhere

Querying Loki is simple enough once you’ve got this “loadDatabase, when it’s done loading then do stuff with the collection” pattern.

./controllers/todo.js

exports.findById = function(request, response){
    db.loadDatabase({}, function(){
        var items = db.getCollection('todo');
         //request params is a string, must be int to lookup properly
        var item = items.findOne({'$loki': request.params.id * 1});
        if (!item) {
            response.sendStatus(404);
            return;
        }
        response.send(item);
    });
}

Oh. Only it’s not.

Gotchya #5: The findOne() method doesn’t work quite as I expected. I had to cast the request parameter string to an integer.

Okay, so that’s still pretty simple, but I have mixed feelings about the behavior of this method. On one hand, coming from a statically typed language, I appreciate that this method used === internally. On the other though, it’s actually pretty surprising behavior in my opinion. In the Javascript world, I would typically expect "1" == 1 behavior here.

Cool! I can query for a specific item now, but I have a problem. My tests are hitting the file system. How am I supposed to mock out the db when my controller requires('lokijs') ?

Gotchya #6: Idiomatic Node doesn’t use Dependency Injection as I know it.

Well, at least not everywhere. Express does use DI for request and response.

exports.index = function(request, response){
  response.send('Hello World!');
}

We pass the request and response objects around as function arguments, which makes faking those out for our tests simple. This is how Dependency Injection is supposed to work and anyone familiar with C# or Java will probably recognize this pattern.

The problem is with the requires() and module system.

var loki = require('lokijs'),
  db = new loki('todo.json');
//...

This is equivalent to creating (“new-ing up”) your dependencies in a class constructor. You can do it, but it makes faking dependencies a nightmare. In other words, you can’t fake your dependencies if you’re creating them from within your module.

My first attempt at breaking the dependency on the file system was to create a constructor. I struggled at this until I finally just gave up and used property injection instead.

./index.js

var express = require('express'),
 todo = require('./controllers/todo');

var loki = require('lokijs');
todo.setDb(new loki('todo.json'));

var app = express();

app.get('/todo', todo.findAll);

./controllers/todo.js

var db;
exports.setDb = function(database) {
 db = database;
}
exports.findAll = function(request, response){
    db.loadDatabase({}, function(){
        var items = db.getCollection('todo');
        response.send(items);
    });
}

This worked, but didn’t feel very good to me. I’ve never really cared for property injection very much. I set out to find the “Node way” to do this. That turned out to be several Saturday morning’s worth of frustration. I’ll spare you that and skip to the solution.

Faking Dependencies with Proxyquire

npm install proxyquire --save-dev

Proxyquire is a great tool. Any struggles I had with it weren’t with it, but with my understanding of the LokiJs object model. You see, I’m accustomed to mocking frameworks like Moq where the library uses some reflection magic to generate fakes for you. Proxyquire works differently. Proxyquire is simply a tool that helps you monkey patch the objects that requires() returns. You have to hand roll your stubs. Which is fine, but it’s important you understand that you have to do handcraft your fakes, particularly if you’re coming from a tool like Moq that does it for you.

The Proxyquire constructor takes in the file path to the module you’re requiring, and your stub object.

proxyquire({string} request, {Object} stubs)

So, a simple example would look something like this.

var mocha = require('mocha'),
 expect = require('chai').expect,
 httpMocks = require('node-mocks-http'),
 proxy = require('proxyquire');

describe('TodoController', function(){
    var todo;

    beforeEach(function(){
        var lokiStub = {}
        todo = proxy('../controllers/todo', {'lokijs': lokiStub});
    }
//...

Of course, this won’t actually work, because the stub we’re passing doesn’t have any of the methods that we’re going to call. We need to build out our stub accordingly. In order to do that, we need to understand the shape of the object returned by new loki('file.json').

This is a view of the loki object, simplified down to just the methods and properties that we’re calling in our todo controller.

var lokiStub = function loki(filename){
    this.loadDatabase = function(options, callback){
        callback();
    };

    testData = [
    {
        "title":"Setup test site on different port.",
        "done":true,
        "$loki":1
    },
    {
        "title":"Finish the todo api",
        "$loki":2
    }];

    this.getCollection = function(collectionName){
        return {
            data : testData,
            findOne : function(query){
                return testData[0];
            }
        }
    }
}

Gotchya #7: Make sure to actually call the callback function from inside of the loadDatabase() stub.

That’s real important, otherwise, you’ll be left scratching your head wondering why your response object’s send method never gets called.  If you recall Gotchya #4, we’re actually performing all of our response writes from inside of the loadDatabase() method’s callback. If you don’t call the callback in your stub….. well, yeah. It’s never going to run. D’oh!

All in all, I think would still prefer to figure out how to do proper constructor injection though. I’m still just a little uncomfortable with monkey patching dependencies.  I’ll return to that at some point in the future, but for now, Proxyquire works great.

Posting & Saving

Now that we can retrieve from our todo controller (and test that it works!) , let’s create a new one and save it to our database. Inserting new documents (i.e. records) into a Loki collection is a simple affair. Just get the collection you want to add to, and insert the object, then save the database file. For simplicity, let’s just insert the body of the post request.

./controllers/todo.js

exports.insert = function (request, response) {
    db.loadDatabase({}, function() {
        var items = db.getCollection('todo');
        var doc = items.insert(request.body);
        db.save();

        response.send(doc);
    });
}

Only, that doesn’t quite work. Loki inserts a new record (document) into the collection, but it’s empty. Firing up the debugger will and inspecting some things will show us that request.body is undefined. What’s going on here? I know I posted data!

Gotchya #8…9? I don’t know. I’ve lost count.: An Express request body is undefined by default. You have to use some middle ware to make that property populate.

Okay…. god bless it, okay….

npm install body-parser --save

./index.js

var express = require('express'),
    bodyParser = require('body-parser');

var app = express();
app.use(bodyParser.json());

And now it works. This middle-ware stuff is kind of magical.

Returning Multiple Formats

I like to be able to surf my API’s in the browser. I got the idea after watching Stefan Tikov’s 2014 GOTO conference presentation, REST: I Don’t Think it Means What You Think it Means. Near the end of the presentation he mentions the idea of returning HTML from your web service endpoints. This works neatly if you’re using a HAL media type and format, or simply returning links to related resources along with your data. Basically, I want to format the data as HTML if the request is coming from a browser. To do that, we’ll need to check the Accept header of the request.

Express’s response.format() method makes it easy to return different formats based on the client’s request, but there are a couple of gotchyas specific to the stack we’ve been working with.

Let’s start by creating an object contains basic information about api. When we browse to the root of the API, it will return information about the web service and the available end points.

./apiInfo.js

// HAL+json description of the API

function ApiInfo() {
    this.Title = 'Todo API';
    this._links = [
            { self: { href: '/' } },
            { todo: [
                {href: '/todo'},
                {href: '/todo/{id}', templated: true}
            ]}
        ];
}

module.exports = ApiInfo;

And we’ll write some test cases for our new controller.

./test/rootControllerTests.js

var mocha = require('mocha'),
    expect = require('chai').expect,
    httpMocks = require('node-mocks-http');

var ApiInfo = require('../ApiInfo');
var root = require('../controllers/root');

describe('RootController', function () {

    describe('index', function(){
         describe('when json requested', function(){
            var request, response;
            beforeEach(function(){
                request = httpMocks.createRequest({
                    headers: {
                        Accept: 'application/json'
                    }
                });
                response = httpMocks.createResponse();
            });

            it('returns a 200 ok', function(){
                root.index(request, response);
                expect(response.statusCode).to.equal(200);
            });

            it('should return ApiInfo', function(){
                var expected = new ApiInfo();

                root.index(request, response);
                var actual = response._getData();

                expect(actual).to.deep.equal(expected);
            });
        });
    });
});

Note that we’re explicitly setting the Accept header for the fake request. Now that we have failing tests, let’s go create the root controller and make them pass.

./controllers/root.js

var ApiInfo = require('../ApiInfo');

exports.index = function (request, response) {
    response.send(new ApiInfo());
}

So far so good, but we want to return HTML when it’s requested, so let’s add a simple test to force us to check the requested mime type.

        describe('when html is requested', function(){
            var request, response;
            beforeEach(function(){
                request = httpMocks.createRequest({
                    headers: {
                        Accept: 'text/html'
                    }
                });
                response = httpMocks.createResponse();
            });

            it('should return ApiInfo', function(){
                var expected = 'Hello World

'

                root.index(request, response);
                var actual = response._getData();

                expect(actual).to.equal(expected);
            });
        });

Which will fail with the message AssertionError: expected { Object (Title, _links) } to equal '<p>Hello World'/p>'. So now we’ll go make it pass.

var ApiInfo = require('../ApiInfo');

exports.index = function (request, response) {

    response.format({
        json: function(){
            response.send(new ApiInfo());
        },
        html: function(){
            response.send('Hello World

');
        }
    });
}

Only… we’re greeted by an error and a test failure. Actually, we’re greeted by lots of them.

1) RootController index when json reqested returns a 200 ok:
     Error: Request object unavailable. Use createMocks or pass in a request object in createResponse to use format.
      at Object.mockResponse.format (node_modules\node-mocks-http\lib\mockResponse.js:586:19)
      at Object.exports.index (controllers\root.js:5:14)
      at Context.&lt;anonymous&gt; (test\rootControllerTests.js:23:22)

  2) RootController index when json reqested should return ApiInfo:
     Error: Request object unavailable. Use createMocks or pass in a request object in createResponse to use format.
      at Object.mockResponse.format (node_modules\node-mocks-http\lib\mockResponse.js:586:19)
      at Object.exports.index (controllers\root.js:5:14)
      at Context.&lt;anonymous&gt; (test\rootControllerTests.js:30:22)

  3) RootController index when html is requested should return ApiInfo:
     Error: Request object unavailable. Use createMocks or pass in a request object in createResponse to use format.
      at Object.mockResponse.format (node_modules\node-mocks-http\lib\mockResponse.js:586:19)
      at Object.exports.index (controllers\root.js:5:14)
      at Context.&lt;anonymous&gt; (test\rootControllerTests.js:51:22)

Gotchya #9 (I went back and recounted): node-mocks-http requires you to pass the the fake request into the fake response if you want to use `response.format()` in your code.

The docs for node-mocks-http are fairly lacking, so even though the error message told me exactly what I needed to do, I had no idea how to do it. It took a long while to figure out that I needed to create a responseOptions object with a req (request) property and pass it as an argument to the httpMocks.createResponse() method. I had initially tried passing the request to the create method and spent a fair amount of time cursing. Long story short, this is how you set up your fake request & response with node-mocks-http so that you can use the response.format() method.

var request, response;
beforeEach(function(){
    request = httpMocks.createRequest({
        headers: {
            Accept: 'text/html'
        }
    });
    responseOptions = {req: request};
    response = httpMocks.createResponse(responseOptions);
});

Green Tests! Whooooooooo!

Time for a View Engine

Okay, fantastic. Now, we could write a bunch of code to concatenate together the HTML with the data from our ApiInfo object, or we can use a view engine. I don’t know about you, but I don’t like re-inventing the wheel, so I was off on the hunt for a good view engine to use. Handlebars looked like a mature solution, and I like the syntax, so let’s use that.

Gotchya #10: Don’t install handlebars. Install hbs.

Handlebars is a templating language, not a view engine. If you just install handlebars, you’re going to have to put the templates in your code or access the file system yourself. That’s not a very clean solution. Instead, use the hbs view engine.

npm install hbs --save

Once you’ve got it installed, we can tell Express to use hbs to render views.

./index.js

var express = require('express'),
bodyParser = require('body-parser');

var app = express();
app.set('view engine', 'hbs');

By default, hbs will look in the ./views/ for templates. Let’s go ahead and create the directory and add a root.hbs file. We’ll keep it simple and return the Title property of our ApiInfo for now.

./views/root.hbs

<h3>{{Title}}</h3>

and update our tests accordingly.

./test/rootControllerTests.js

it('should return ApiInfo', function(){
var expected = '
<h3>Todo API</h3>
';

root.index(request, response);
var actual = response._getData();

expect(actual).to.equal(expected);
});

Run the tests to watch it fail.

AssertionError: expected '<p>Hello World</p>' to equal '<h3>Todo API</h3>'

Now let’s update our controller to render the view whenever html is requested.

./controllers/root.js

var ApiInfo = require('../ApiInfo');

exports.index = function (request, response) {

  response.format({
    json: function(){
      response.send(new ApiInfo());
    },
    html: function(){
      response.render('root', new ApiInfo());
    }
  });
}

Now our test should pass, so let’s run them again.

AssertionError: expected '' to equal '<h3>Todo API</h3>'

Ummmm….. what?

I fire up the server and browse to the root of the site on local host and can see that the view was being properly rendered and returned, so why is response._getData empty?!

 Gotchya #11: Our tests aren’t using the view engine, so no html is written to the response during our test.

It took me longer than it should have to realize this. Of course the response doesn’t have any data after we call render. There isn’t a view engine registered for the test. Hell, our tests don’t even know about Express!

These are Good Things™ in my opinion. We don’t really want to test the output of the view engine, as the content and format is likely to change, but I’d still like to validate that the controller is doing what I expect it to do. The question is, how do I do that?

Again, the documentation for node-mocks-http is terribly lacking, but I was able to find a solution by looking directly at the code. node-mocks-http exposes methods for retrieving the view name and the data that was passed to it. We can validate that the controller is passing the right things to the view engine by calling the response._getRenderView and response._getRenderData methods.

./test/rootControllerTests.js

it('should use the root view', function(){
  root.index(request, response);
  expect(response._getRenderView()).to.equal('root');
});

it('should return ApiInfo', function(){
  root.index(request, response);
  expect(response._getRenderData()).to.deep.equal(new ApiInfo());
});

With our tests passing, let’s finish up by making our view template loop through all the todo items and turn them into links.

./views/root.hbs

<h3>{{Title}}</h3>
<ul>
{{#each _links[1].todo}}
{{#if templated}}
	<li>{{href}}</li>
{{else}}
	<li><a href="{{href}}">{{href}}</a></li>
{{/if}}
{{/each}}</ul>

Fire the server up and navigate to local host and….. the gnarliest error message I’ve seen in a long while.

Parse error on line 3:
...
<ul> {{#each _links[1].todo}}
---------------------^
Expecting 'CLOSE_RAW_BLOCK', 'CLOSE', 'CLOSE_UNESCAPED', 'OPEN_SEXPR', 'CLOSE_SEXPR', 'ID', 'OPEN_BLOCK_PARAMS', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', 'SEP', got 'INVALID'
at...

Whiskey. Tango. Foxtrot.

Gotchya #12: Handlebars has a weird syntax for accessing array elements. You have to “dot into” the index.

This is the correct way to access a specific array element in a handlebars template.

 {{#each _links.[1].todo}}

At long last, we have a fully functional web service that we can also discover by surfing in our browser.

TL;DR

If you’ve stuck with me this far, I feel like you deserve a summary of this post and what I’ve learned over the last few weeks. First, here’s my package.json file. This represents the stack I’m using as of now.

{
  "name": "todo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "mocha"
  },
  "author": "Christopher J. McClellan",
  "license": "",
  "devDependencies": {
    "chai": "^3.5.0",
    "mocha": "^3.1.2",
    "node-mocks-http": "^1.5.4",
    "proxyquire": "^1.7.10"
  },
  "dependencies": {
    "body-parser": "^1.15.2",
    "express": "^4.14.0",
    "handlebars": "^4.0.6",
    "hbs": "^4.0.1",
    "lokijs": "^1.4.1"
  }
}

You should be able to basically copy/paste this into your project, run npm install and be up and running with the same stack I’m using here.

Here’s the list of “Gotchyas” I came across:

  1. If your test modules aren’t under the ./test/ directory, Mocha won’t find them.
  2. Don’t use express-mocks-http, use node-mocks-http instead.
  3. If you’re using VS Code, add a launch configuration that runs mocha so you can debug your tests.
  4. Javascript is ruthlessly asynchronous. Don’t expect things to work in your code exactly the way they worked in the node terminal.
  5. LokiJs’s findOne() method requires an integer if you’re going to match on the $Loki id. Cast the query string param to an int.
  6. Idiomatic NodeJs doesn’t use dependency injection as a Java or C# developer would think of it. It uses monkey patching. Use a tool like Proxyquire to break module dependencies in your tests.
  7. If you’re faking a method that takes a callback, be sure to actually call it in your stub method.
  8. If your service accepts post data, you’re going to need to use some body parsing middleware.
  9. node-http-mocks requires you to pass the request to the response if you’re going to use the response.format() method in your code.
  10. Don’t install handlebars directly. Install the hbs view engine instead.
  11. If your tests aren’t using the view engine (and they shouldn’t be), the mock response won’t have any data in it. Use the http mock’s _getRenderView and _getRenderData to test your controllers.
  12. Handlebars has a strange syntax for accessing specific array elements. You can’t array[index].property. You must array.[index].property instead.

All in all, most of my issues were with getting my unit tests to behave the way I wanted them to. If I hadn’t insisted on doing this TDD, I probably would have had a much easier go of it. Even if I had just not insisted on isolating my tests from the file system and node server I would have had an easier go at it, but I wouldn’t have felt good about any of it. Now I feel like I could confidently say to a co-worker, “Yeah. We could do that in Node. I know a pretty good stack we could use.” Really, the goal wasn’t to create a Todo service for my site, it was to know that I could use Node in a real world project and feel good about it. Mission Accomplished.

What’s next?

I haven’t had time to get that far yet, but I really liked Morris Singer had his tests set up in his presentation. (Slides Here). His use of promises really seemed to clean some things up and I’d like to learn a bit more about that. Promises seem to have a lot in common with the async-await features of .Net, so I have a feeling I’m going to love q and chai-as-promised.

, , , , , , , ,

  1. Leave a comment

Leave a comment