Last active
February 12, 2019 03:41
-
-
Save tkissing/e5fa4908150c82d73131 to your computer and use it in GitHub Desktop.
Singleton JavaScript modules that are easy to test! (Hint: Avoid object literals)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// using AMD syntax here to avoid confusion about scope and file boundaries, | |
// but the concept translates 1:1 to CommonJS (node/io) modules | |
define('badSingleton', function(require, exports, module) { | |
var dependency = require('dependency'); | |
var privateState; | |
module.exports = { | |
foo: function() { | |
return privateState; | |
}, | |
bar: function() { | |
privateState = dependency(privateState ? 'bar': 'baz'); | |
}, | |
baz: function() { | |
if (privateState) { | |
return Promise.resolve(privateState); | |
} | |
return Promise.reject(); | |
} | |
} | |
}); | |
// once you call .bar, there is no way to reset the internal state of badSingleton. | |
// The order of your unit tests now matters! | |
// also, mocking dependency is non-trivial in both node and the browser | |
define('betterSingleton', function(require, exports, module) { | |
var dependency = require('dependency'); | |
function BetterSingleton(dependency) { | |
// move ALL the state inside the constructor | |
var privateState; | |
this.foo = function() { | |
return privateState; | |
}; | |
this.bar = function() { | |
privateState = dependency(privateState ? 'bar': 'baz'); | |
}; | |
this.baz = function() { | |
if (privateState) { | |
return Promise.resolve(privateState); | |
} | |
return Promise.reject(); | |
} | |
} | |
module.exports = new BetterSingleton(dependency); | |
}); | |
// now in your tests you can write | |
// var BetterSingletonConstructor = require('betterSingleton').constructor; | |
// and create as many instances as you have tests, each starting with a clean state. | |
// and since we moved the dependency into a constructor argument | |
// the tests can make it with ease if needed |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment