Intro
The Revealing Module Pattern, RMP for short, is a popular pattern used in Javascript development. The idea behind it is that it conceals implementation details within a closure and 'reveals' only those functions which are intended to be public through an object literal returned from the containing function. The pattern was developed by Christian Heilmann and is written about by Addy Osmani in his book Learning Javascript Design Patterns. Because it presents a public API, it would appear that the RMP pattern would lend itself well to unit testing and whilst this is true there are some caveats to consider and I want to talk about these in this article.
var myModule = (function () {
function foo() {
return 'this is foo';
}
function bar () {
return 'this is bar';
}
return {
foo : foo,
bar : bar
};
}());
console.log(myModule.foo()); // this is foo
Lets imagine that we are writing tests for the above module, perhaps using a Javascript unit testing library like Jamine. We might do something like this:
expect(myModule.foo()).toBe('this is foo');
This is a good test: we call a function, get back a value, and verify that it is what we expect it to be. So far so good. But what if in the implementation of foo() we were to make a call to another function within the closure; bar(), for example?
function foo() {
return bar() + 'this is foo';
}
Since the aim of a unit test is to test things in isolation, we might want to mock out the implementation of bar() since it may contain some complicated or long running computation that we don't want to worry about when testing foo(), and presumably we are also already testing it somewhere else anyway. Lets mock out bar() using monkey patching. Normally, if we were using Jasmine, we would use a spy for this, but I want to make it explicit what we're doing.
// save old version of bar so we can restore later
var oldBar = myModule.bar;
var mockBar = function () {
/* implementation here */
}
myModule.bar = mockBar;
This doesn't work however. When foo() runs it simply calls the real implementation of bar() within the closure.
The functions within our API are really just proxies which stand in for the actual functions. By mocking out the API functions we are simply breaking the link between the proxies and the proxied functions.
This perhaps exposes a problem with the RMP but maybe it also exposes a problem with our code. A function in the API calling into its own API seems a bit strange; It means that our API is dependent on itself!
Since foo and bar are clearly sharing the same code it makes sense to move this shared code into a private function so that one API function is not dependent on another.
var myModule = (function () {
function foo() {
return _privateBar() + 'this is foo';
}
function bar () {
return _privateBar();
}
function _privateBar() {
return 'this is bar';
}
return {
foo : foo,
bar : bar
};
}());
This has improved our code a lot but what we have done is made inaccessible the code that we wished to mock. So is this a good solution? Yes, it is. In unit testing we need to consider what the actual 'unit' consists of. We should test the unit itself in full and only mock out external dependencies. Private functions are an implementation detail and should be able to vary without breaking the test. They themselves should not be tested directly but as part of the unit when we test the public API. For this reason, we should not have been attempting to mock out bar in the first place.
But what about the case where the code within _privateBar() is doing some long complicated computation, or perhaps even calling out to a remote server?
This leads us to our next Eureka moment. It may be that our private function does not belong in our module at all. It would be better taken out into it's own module where it can be tested in isolation as well as being reused by other modules besides our current one.
var barService = getBarService();
var myModule = (function () {
function foo() {
return barService() + 'this is foo';
}
function bar() {
return barService();
}
return {
foo : foo,
bar : bar
};
}());
Now we can easily mock out barService when testing foo() and we can test the barService itself elsewhere if we wish to. Note that bar() is a simple pass-through; it does nothing but call barService() and return the value from that call. This is fine though since it means that users of this module do not have to have a direct dependency on barService. In design pattern terms you could say that our module has became a 'facade'.
Conclusion
The refactoring has significantly improved our code; increased modularity and reusability, making it conform more closely to the single responsibility rule, and more easily testable. These gains came about because we wanted to write unit tests. This demonstrates that unit testing amongst its other benefits can help the programmer to write higher quality code.