Quantcast
Channel: How To Node
Viewing all articles
Browse latest Browse all 134

Testing Private State and Mocking Dependencies

$
0
0

During Christmas I've been working on Testacular and found some tricks how to make my testing life easier. It's nothing special at all, just a simple way how to access private state of a module and how to mock out some dependencies. I've found these two techniques pretty usefull, so I believe it might help someone else as well...

Why would you need to access private state of a module?

Private should be private, right? Yes, for sure. But during unit tests, it can be very helpful to have access to private state of a module - I always try to cover functionality or bug at the lowest possible level, because it's simply cheaper:

  • faster test execution
  • less code is required to bootstrap the test

Let's say we are building very simple static web server, the skeleton might look something like this:

var http = require('http');

var handleRequest = function(request, response) {
  // read file from fs and send response
};

exports.createServer = function() {
  return http.createServer(handleRequest);
};

This module has only one public method createServer, so unless we make it public, we can't get hold of anything else but this method. That sucks, because HttpServer doesn't have any public method to call the handler, so we would have to send some data through socket to test it. That's way too much effort, especially when you realize that the only code we really need to test is thehandleRequest function - everything else is just Node and we trust Node, because it's awesome. We need to test our code - that's where all the bugs are.

Why would you need to mock out dependencies?

Some dependencies are cheap, some not. When our code uses modules like util or path, we are fine. Nothing bad happens there. But when it comes to some other modules like fs, net or http, it's totally different story. We simply don't want to deal with real filesystem in unit tests. There are many reasons for that, such as:

  • accessing file system is slow
  • it requires seting up some state of filesystem
  • there is only one instance of filesystem, so conflicts between different unit tests might happen

So we want our module to use something different - we call these objects test doubles (I actually like using mock/stub/dummy definition from G.Meszaros). The question is, how can we persuade our awesome module, to use a different instance during testing and different instance in production?

Dependency Injection is great for this - it wires all the pieces together (yep, it saves us lot of work) - and more than that, it does allow us to use different instances during testing. Yep, DI is just awesome! I actually think, that new languages such as Dart should support DI natively - in the same way as they do support memory management.

Unfortunately, there is no DI in Node, at least I haven't found any sufficient implementation. Writing a Dependency Injection framework is definitely a solution, but I was looking for something faster...

Let's do it !

Module Loader

This is actually the code this post is all about :-D

Instead of using Node's require, we use loadModule function, which reads the content of requested module (javascript source file) and executes it on the context object. So all the private state of the module is dumped into the context object and yay, we can access everything! See vm.runInNewContext for more info.

Inside this context object, we defined our own require function, which means whenever the module asks for a dependency, our loadModule will be called intead of Node's require. That's pretty cool, because we can decide, whether we want to return a mock or real module, in which case we delegate the request to Node's require.

Very simple web server example

Let's use it in test now

This is very simple example of unit testing web-server module, using loadModule function.

We can access both private functions as properties of module now, which is great, because we can add more tests very easily. For example, you might have noticed, that extensionFromUrl won't return correct extension when requested url contains query param. Piece of cake, just add a test that covers this bug:

it('extensionFromUrl() should ignore query params', function() {
  expect(module.extensionFromUrl('/some.html?param=ignored')).toBe('html');
});

The second test only asserts whether we set proper status code for existing file. We should assert status code for non existing file as well as caching headers, content type header and many other stuff. The important point here is, that it's fast, because it doesn't touch the real filesystem and still does test what needs to be tested - our code.


Viewing all articles
Browse latest Browse all 134

Trending Articles