Overview
I've been using the Revealing Module Pattern for a few months and I'm looking for an alternative or variation on this pattern that will solve both issues I'm currently having with event handlers and testability. I know I could come up with some combination of what I have below to solve my problem, but I'm hoping to find a clean alternative that I could use consistently that addresses both of my current concerns.
Revealing Module Pattern
So in this example, I have no issues with event handlers, but I can't mock calls to functions
within functions to test in isolation:
var Lion = (function () {
// Reference to rawr works as expected when the click event is triggered
function watch() {
document.addEventListener('click', rawr);
}
function rawr() {
console.log('rawr');
}
function attack() {
console.log('attack');
}
/*
* Can't test goCrazy() in isolation. Mocking rawr() and attack()
* has no effect as demonstrated below.
*/
function goCrazy() {
rawr();
attack();
// Important "crazy" logic
}
return {
watch: watch,
rawr: rawr,
attack: attack,
goCrazy: goCrazy
};
}());
module.exports = Lion;
Example Test Case (Jasmine)
describe('Mock Check', function () {
it('should mock rawr() and attack() and test only goCrazy logic', function () {
var lion = require('Lion');
spyOn(lion, 'rawr').and.reutrnValue(true);
spyOn(lion, 'attack').and.reutrnValue(true);
var crazy = lion.goCrazy();
expect(lion.rawr).toHaveBeenCalled(); // <-- Fails
expect(lion.attack).toHaveBeenCalled(); // <-- Fails
// can't test goCrazy() logic in isolation :(
});
});
Same Module using this instead and invoked using new
In this example, I can successfully mock calls to functions within functions, but if I attempt to add an event handler, this becomes undefined when the event is triggered.
var Lion = function () {
// Reference to 'this' becomes undefined when event is triggered
this.watch = function () {
document.addEventListener('click', this.rawr);
}
this.rawr = function () {
console.log('rawr');
}
this.attack = function () {
console.log('attack');
}
/*
* Can successfully test goCrazy() in isolation by being able to mock
* rawr() and attack() as needed
*/
this.goCrazy = function () {
this.rawr();
this.attack();
// Important "crazy" logic
}
};
module.exports = Lion;
Example Test Case (Jasmine)
describe('Mock Check', function () {
it('should mock rawr() and attack() and test only goCrazy logic', function () {
var Lion = require('Lion');
var lion = new Lion();
spyOn(lion, 'rawr').and.reutrnValue(true);
spyOn(lion, 'attack').and.reutrnValue(true);
var crazy = lion.goCrazy();
expect(lion.rawr).toHaveBeenCalled(); // <-- Success
expect(lion.attack).toHaveBeenCalled(); // <-- Success
// testing goCrazy logic in isolation :)
});
});
Thanks for your time. If any clarification is necessary, let me know and I'll modify my post.
The actual problem here is that, the event handler loses the context of the current object. You can bind it like this
document.addEventListener('click', this.rawr.bind(this));
This will make sure that whenever the rawr is invoked, the this inside rawr corresponds to the lion object which you created.
Related
Let's say, I have a function:
function consoleOutput(param) {
var newParam = param * param;
console.log(newParam);
}
How can I test in Mocha, that this function will be working correctly (param will be multiplied by two and output to the console). Thanks.
A great library for these types of tests is Sinon. It can be used to "hook" existing functions and track how those functions get called.
For example:
const sinon = require('sinon');
const assert = require('assert');
// the function to test
function consoleOutput(param) {
var newParam = param * param;
console.log(newParam);
}
it('should log the correct value to console', () => {
// "spy" on `console.log()`
let spy = sinon.spy(console, 'log');
// call the function that needs to be tested
consoleOutput(5);
// assert that it was called with the correct value
assert(spy.calledWith(25));
// restore the original function
spy.restore();
});
The advantage of this is that you don't need to change the original function (which, in this case, isn't a big deal, but may not always be possible in larger projects).
I have a requirejs module created with the following pattern:
// foo.js
define(function(){
var x = 0;
function doStuff(){
return ++x;
}
return { doStuff: doStuff };
});
And a QUnit test which looks something like this:
// testfoo.js
define(['QUnit, foo'], function(QUnit, foo){
function setup(){
//...?
}
function teardown(){
//...?
}
function runTests(){
QUnit.test('foo counter remains at 1 on multiple tests', function(assert){
assert.equal(foo.doStuff(), 1); // this will work
});
QUnit.test('foo counter remains at 1 on multiple tests', function(assert){
assert.equal(foo.doStuff(), 1); // this obviously won't
});
}
return runTests;
});
How can I reset foo for each test?
I would like to keep foo a revealing module, i.e. not converting it to a constructor function with an altered prototype.
I tried var foo = require('foo');, but since requirejs is AMD based, it will complain about things getting loaded in the wrong order.
I suggest checking out SquireJS to create an isolated context for your tests. Squire is designed to inject mock dependencies by creating an isolated RequireJS context. A side effect of this is that the 'foo' library will be reload each time you call injector.require(), resetting the state of your library. The only downside is that your tests will need to be asynchronous.
// testfoo.js
define(['Squire', 'foo'], function (Squire, foo) {
'use strict';
QUnit.start();
QUnit.module('foo', {
setup: function () {
},
teardown: function () {
}
});
QUnit.test('Testing foo counter in main context.', 2, function (assert) {
assert.equal(foo.doStuff(), 1); // this will work
assert.equal(foo.doStuff(), 2); // this should work also.
});
QUnit.test('Testing foo counter in context created by Squire.', 1, function (assert) {
var done = assert.async(),
injector = new Squire();
injector.require([
'foo'
], function (foo) {
assert.equal(foo.doStuff(), 1); // this will work.
done();
});
});
});
I've posted a sample project here: qunit-squirejs
While this certainly isn't the cleanest way about it, by saving the function as a string, and recreating the function before each test, you can accomplish the same effect.
var fooString = foo.toString();
QUnit.testStart(function() { eval('foo = ' + fooString});
What is the best way to test IIFE (Immediately Invoked Function Expression) that calls itself recursively with setTimeout:
(function myFuncToBeTested() {
// Code to be tested
...
setTimeout(myFuncToBeTested, timeout) // timeout should be checked
})()
I found the following solution that replaces global setTimeout function with own stub. This has following issues:
// Saving original setTimeout. This should be restored in test cleanup
originalSetTimeout = global.setTimeout
// Replace with function
global.setTimeout = function setImmediate(myFunc, interval) {
// FIXME: This function now always called
// Save interval to be tested
savedInterval = interval
}
could this function be made into an object?
var myObject = (function(){
function start(){
myFuncToBeTested();
setTimeout(start, 10);
return this;
}
function myFunctToBeTested(){
//Code to be tested
}
return {
start: start,
myFuncToBeTested: myFuncToBeTested
}
})().start();
and then you could use testing framework of your choice to test:
assert( myObject.myFuncToBeTested() == expectedValue );
I want to suggest a hybrid solution between thedarklord47's answer and your experiments with stubbing setTimeout. An IIFE like you have is inherently difficult to test, since you left no approach by which to check if it has been called. You can modify your API as follows:
var repeater = {
start: function () {
this.func();
setTimeout(this.start.bind(this), timeout);
},
func: function () {
// code to be tested
}
};
Then your test can look something like this (since you tagged with sinon I have used it, and in particular its fake timer API which will allow you to check your interval functionality):
// setup
var clock = sinon.useFakeTimers();
var spy = sinon.spy(repeater, 'func');
// test
repeater.start();
assert(spy.calledOnce);
// advance clock to trigger timeout
clock.tick(timeout);
assert(spy.calledTwice);
// advance clock again
clock.tick(timeout);
assert(spy.calledThrice);
// teardown
clock.restore();
spy.restore();
I am using a database library that its callback-based interface looks like this:
var DB = {
insert: function(options, callback) {
}
}
I want to implement a wrapper around this database to convert its callback style API to a promise based API. To do this I have defined the following class:
var DatabaseWrapper = {
init: function(db) {
this.db = db;
},
insert: function(options) {
return Q.denodeify(this.db.insert.bind(this.db))(options);
}
}
I want to write a unit test to ensure that when I call DatabaseWrapper.insert it calls DB.insert. So far my test looks like this:
describe('DatabaseWrapper', function () {
var wrapper, insertSpy, bindStub;
beforeEach(function () {
wrapper = Object.create(DatabaseWrapper);
insertSpy = sinon.spy(function () {
console.log('insertSpy got called');
});
bindStub = sinon.stub();
wrapper.db = {
insert: function (options, callback) {
}
};
sinon.stub(wrapper.db.insert, 'bind').returns(insertSpy);
});
describe('#insert', function () {
it('should delegate to db.insert', function (done) {
wrapper.insert({herp: 'derp'});
expect(wrapper.db.insert.bind).to.have.been.calledOnce;
// This fails but I expect it to succeed
expect(promise).to.have.been.calledOnce;
})
});
});
The DB instance's insert method is actually getting called as after the test fails, as the 'insertSpy got called' message is printed in the console.
But apparently it gets called after the test has failed.
As far as I know, this is due to the way Node's process.nextTick works. So the call to the callback happens after the test fails. Is there a way I can fix this test without relying on third-party libraries (e.g. q-flush)?
You're performing an asynchronous action so it's best to perform an asynchronous test. Adding a setTimeout still leaves you prone to race conditions.
describe('#insert', function () {
it('should delegate to db.insert', function () { // no done here
// note the return here to signal to mocha this is a promise test
return wrapper.insert({herp: 'derp'}).then(function(){
// add expects here, rest of asserts should happen here
expect(wrapper.db.insert.bind).to.have.been.calledOnce;
});
})
});
});
Coming from the Java (OOP) world, I am used to classes, inheritance and multi threading. Now for my little walkabout in the JavaScript domain, I try to utilize these paradigms and patterns where applicable. Read: use prototypes ("classes" / objects) and WebWorkers for parallel execution. However, this one case does not work ...
HTML site starting a worker:
<html>
<head>
<script>
var worker = new Worker("worker.js");
worker.onmessage(event) {
// here be fancy script
}
worker.postMessage("run, worker, run!");
</script>
</head>
...
</html>
Worker called by HTML ("worker.js"):
self.loadScripts("handler.js");
var handler = null;
self.onmessage = function(event) {
if(!handler) {
handler = new Handler();
}
handler.compute();
}
The Handler as called by the worker ("handler.js"):
function Handler() {
}
Handler.prototype = {
compute: function() {
this.doSomething(); // <-- ERROR! "this" points to the worker context,
// not to the Handler instance. So "doSomething" is
// undefined. However, the following line would work:
// Handler.prototype.doSomething();
},
doSomething: function() {
// More code here
}
}
Is JavaScript prototyping and "inheritance" meant to work this way? Should I always use the prototype property instead of this? What if I want to access this.myProperty instead of a function?
Also: is there any reasonable way to bind this to the Handler instance in the constructor? At least the code is not cluttered with lengthy Handler.prototype references.
Thanks!
Thank you for your comments. Indeed, the context of this works as expected. The real code used a timeout callback:
Handler.prototype = {
compute: function() {
self.setTimeout(this.doSomething, 1000); // Here the this got lost
},
doSomething: function() {
// Code here
}
}
It seems this from a timeout call is referencing the worker context. To solve the issue, I just wrapped the callback in an anonymous function (referencing the caller as a variable, as jfriend00 suggested):
Handler.prototype = {
compute: function() {
var caller = this;
self.setTimeout(function() { // Wrap for great justice
caller.doSomething();
} , 1000);
}, doSomething: function() {
// Code here
}
}
Thanks again.