I'm moving my JavaScript test code from Jest to Mocha. One nice feature of Jest is that it automatically generated stubs for your modules. These stubs implement the same API as the originals, but all their functions return undefined. Exported classes are also stubbed, i.e. they have all the same methods as the original, but the return undefined:
// mymodule.js
var MyClass = function() { this.foo = 42; };
MyClass.prototype.getFoo = function() { return this.foo; };
module.exports = {
myclass: MyClass,
myfunction: function() { return 42; }
};
// __tests__/mymodule-test.js
var mm = require('../mymodule'); // Jest auto-mocks this module.
describe('test', function() {
it('functions and constructors are mocked', function() {
expect(mm.myfunction()).toEqual(undefined); // function is stubbed
var mc = new mm.myclass();
expect(mc.getFoo()).toEqual(undefined); // fn on prototype is stubbed.
});
});
For various reasons, I'm moving over to MochaJS but I'd like to keep this stubbing behavior. I can inject stubs for modules using proxyquire. But I need to define the stubs myself.
What I'd like is a function which which takes a Node module and returns something like the Jest auto-mocked version of the module. Jest's code to do this is in moduleMocker.js. I've written some code of my own to do this (included below). But it's quite tricky and doesn't feel like code I should be writing.
Is there a standard library for doing this?
Here's what I've written:
// stubber.js
var U = require('underscore');
function replaceFunctionsWithStubs(rootObj) {
var replacedObjs = []; // array of [original, replacement] pairs.
function previousReplacement(obj) {
for (var i = 0; i < replacedObjs.length; i++) {
if (replacedObjs[i][0] == obj) {
return replacedObjs[i][1];
}
}
return null;
}
function replacer(obj) {
var t = typeof(obj);
if (t != 'function' && t != 'object') {
// Simple data.
return obj;
}
// Return previous mock to break circular references.
var prevRep = previousReplacement(obj);
if (prevRep) return prevRep;
if (t == 'function') {
var f = function() {};
replacedObjs.push([obj, f]);
if (!U.isEmpty(obj.prototype)) {
// This might actually be a class. Need to stub its prototype, too.
var newPrototype = replacer(obj.prototype);
for (var k in newPrototype) {
f.prototype[k] = newPrototype[k];
}
}
// Stub any properties the function might have.
for (var k in obj) {
f[k] = replacer(obj[k]);
}
return f;
} else if (typeof(obj) == 'object') {
// TODO: Do I need to handle arrays differently?
var newObj = {};
replacedObjs.push([obj, newObj]);
for (var k in obj) {
newObj[k] = replacer(obj[k]);
}
return newObj;
} else {
return obj; // string, number, null, undefined, ...
}
}
return replacer(rootObj);
}
module.exports = function(m) {
return replaceFunctionsWithStubs(m);
};
Related
Let's say I have a function named fna() that does a simple thing such as:
var fna = function(ar) {
console.log("argument: ", ar);
return "return value is argument too: " + ar;
}
fna() is coded by some other developer and I can't access to it. He didn't bother casting any events and when it is called, I have to be aware of it. Hopefully, his method is accessible by window.fna().
I want some additional code to be executed. Let's say, add this console.log
var fna = function(ar) {
console.log("Hola, I am some additional stuff being rewired");
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
And I want this to be executed even when called from fnb() by some other part of the code.
var fnb = function() {
return fna("Bonjour, I am fnb and I call fna");
}
Here is a way I found, using the utils.rewire() method. utils is just some utility belt, and it could be added to your favorite framework as a plugin. Unfortunately, it only works on Firefox.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(functionFullName, callback) {
var rewired = window[functionFullName];
console.log("%s() is being rewired", functionFullName)
window[functionFullName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
Use it like this.
utils.rewire("fna",function(){
console.log("Hola, I am some additional stuffs being rewired");
});
This seems to work such as shown in this jsbin, but (and here is my question:) How do I rewire obja.fna()?
var obja = {
fna = function(ar) {
console.log("argument:", ar);
return "return value is argument too: " + ar;
}
};
I cannot make it work to rewire the some.object.method() method.
Extra bonus question: Is there a more cleaner way to do this? Out-of-the-box clean concise and magic library?
Refactor rewire into a rewireMethod function which acts on any given object:
var utils = utils || {};
utils.rewireMethod = function (obj, functionName, prefunc) {
var original = obj[functionName];
obj[functionName] = function () {
prefunc();
return original.apply(this, arguments);
};
};
Note that rewire can now be written as:
utils.rewire = function (functionName, prefunc) {
utils.rewireMethod(window, functionName, prefunc);
};
Then you just call it as:
utils.rewireMethod(obja, "fna", function () {
console.log("Hola, I am some additional stuff being rewired");
});
Note that nothing special is required if you have a method like window.ideeli.Search.init(). In that case, the object is window.ideeli.Search, and the method name is init:
utils.rewireMethod(window.ideeli.Search, "init", function () {
console.log("Oh yeah, nested objects.");
});
Add a parameter to rewire that is the object containing the function. If it's a global function, pass in window.
var utils = utils || {};
// let's rewire a function. i.e. My.super.method()
utils.rewire = function(object, functionName, callback) {
var rewired = object[functionName];
console.log("%s() is being rewired", functionName)
object[functionName] = function() {
callback();
return rewired.apply(this, arguments);
}
}
utils.rewire(some.object, "method", function(){} );
You can simply use a closure to create a generic hook function that allows you to specify another function to be called immediately before or after the original function:
function hookFunction(fn, preFn, postFn) {
function hook() {
var retVal;
if (preFn) {
preFn.apply(this, arguments);
}
retVal = fn.apply(this, arguments);
if (postFn) {
postFn.apply(this, arguments);
}
return retVal;
}
return hook;
}
So, for any function that you want to hook, you just call hookFunction and pass it the function you want to hook and then an optional pre and post function or yours. The pre and post function are passed the same arguments that the original function was.
So, if your original function was this:
var fna = function(ar) {
console.log("argument:",ar);
return "return value is argument too:"+ar;
}
And, you want something to happen every time that function is called right before it's called, you would do this:
fna = hookFunction(fna, function() {
console.log("Hola, I am some additional stuff being rewired right before");
});
or if you wanted it to happen right after the original was called, you could do it like this:
fna = hookFunction(fna, null, function() {
console.log("Hola, I am some additional stuff being rewired right after");
});
Working demo: http://jsfiddle.net/jfriend00/DMgn6/
This can be used with methods on objects and arbitrary nesting levels of objects and methods.
var myObj = function(msg) {
this.greeting = msg;
};
myObj.prototype = {
test: function(a) {
log("myObj.test: " + this.greeting);
}
}
var x = new myObj("hello");
x.test = hookFunction(x.test, mypreFunc2, myPostFunc2);
x.test("hello");
Based on Claudiu's answer, which seems to be the most appreciated way, here is a solution using a for loop and proxying the context... But still, I find this ugly.
var utils = utils || {};
// Let's rewire a function. i.e. My.super.method()
utils.rewire = function(method, callback) {
var obj = window;
var original = function() {};
var tree = method.split(".");
var fun = tree.pop();
console.log(tree);
// Parse through the hierarchy
for (var i = 0; i < tree.length; i++) {
obj = obj[tree[i]];
}
if(typeof(obj[fun]) === "function") {
original = obj[fun];
}
var cb = callback.bind(obj);
obj[fun] = function(ar) {
cb();
return original.apply(this, arguments);
}
}
Well, this looks strange. Consider this
function wrap(fn, wrapper) {
return function() {
var a = arguments;
return wrapper(function() { return fn.apply(this, a) })
}
}
Example:
function foo(a, b) {
console.log([a, b])
return a + b
}
bar = wrap(foo, function(original) {
console.log("hi")
var ret = original()
console.log("there")
return ret
})
console.log(bar(11,22))
Result:
hi
[11, 22]
there
33
To wrap object methods, just bind them:
obj = {
x: 111,
foo: function(a, b) {
console.log([a, b, this.x])
}
}
bar = wrap(obj.foo.bind(obj), function(fn) {
console.log("hi")
return fn()
})
Lets say I have a controller in AngularJS:
myApp.controller('SearchController',
function ($scope, UserService) {
// for intellisense, UserService is undefined here
var user = UserService.getUsers().then(function(data){
// yada yada
}, function(err){
// yada yada
});
});
However, in my intellisense file, I can dynamically inject UserService to get its functions like this:
intellisense.addEventListener('statementcompletion', function (event) {
// tried doing this, but doesn't work!
// event.target = {};
var injector = angular.injector(['ng', 'myApp']);
var dependency = injector.get(event.targetName);
event.items = [];
for (method in dependency) {
intellisense.logMessage(method);
event.items.push({ name: method, kind: 'field', value: function () { } });
}
});
Now, if I have a global variable (or function variable) defined as UserService = {} and inside my controller function I type UserService. I will get a pop up of all the functions in the service. But if I don't have it defined, since it is interpreted as undefined by intellisense, it can't show me the options even though statementcompletion is working (as seen in the Javascript Language Service console).
My question is, apart from annotating the function, is there anyway to define UserService as an object in the intellisense file? Defining event.target = {} does not work (see intellisense code above).
One way that works is to "call" the component functions (controller, services, etc) from intellisense code with empty objects.
I am sure this can be a lot cleaner but here's what I've done:
https://github.com/diwasbhattarai/angularjs-intellisense
By John Bledsoe: https://github.com/jmbledsoe/angularjs-visualstudio-intellisense/
references.js - add this file as reference in Tools>Options>TextEditor>Javascript>Intellisense>References
/// <reference path="../source/lib/assetBundle.js" />
/// <reference path="_moduleDecorator.js" />
/// <reference path="_componentDecorator.js" />
/// <reference path="../source/app/appBundle.js" />
intellisense.addEventListener('statementcompletion', function (event) {
// for core angular objects
addComponentToIntellisense('ng');
// for custom objects in application modules
for (var moduleIndex in modules) {
addComponentToIntellisense(modules[moduleIndex]);
}
function addComponentToIntellisense(module) {
var $injector = angular.injector(['ng', module]),
dependency = $injector.get(event.targetName),
dep;
if (typeof dependency === "function") dep = new dependency();
else dep = dependency;
for (var method in dep) {
event.items.push({ name: method, kind: 'field', value: dependency[method] });
}
}
});
_moduleDecorator.js - to keep track of all the modules in your app
//_moduleDecorator
(function () {
var originalModule = angular.module;
// TODO change to array
modules = {};
var rep = false;
var count = 0;
angular.module = function () {
for (var k in modules) {
if (modules[k] === arguments[0]) {
rep = true;
break;
}
}
if (!rep) modules[count++] = arguments[0];
return originalModule.apply(angular, arguments);
};
})();
_componentDecorator.js - to "call" component functions with empty object parameter
(function () {
// pick all the components in all modules and initialize them for intellisense
for (var moduleIndex in modules) {
var currentModule = angular.module(modules[moduleIndex]),
queue = currentModule._invokeQueue,
// add other components such as value, provider, etc later
angularComponents = ['controller', 'factory', 'service', 'value'];
for (var i = 0; i < angularComponents.length; i++) {
var currentComponent = angularComponents[i],
originalComponentFn = currentModule[currentComponent];
currentModule[currentComponent] = (function (currentModule, queue, originalComponentFn) {
return function () {
originalComponentFn.apply(currentModule, arguments);
initializeComponents(queue);
};
})(currentModule, queue, originalComponentFn);
}
}
function initializeComponents(queue) {
var elem = queue.filter(function (element) {
var componentName = element[2][0].toLowerCase();
return (componentName.indexOf(componentName) !== -1);
});
for (var i = 0; i < elem.length; i++) {
var tempComp = elem[i][2][1],
compFunc;
// for array notation for DI
if (typeof tempComp !== "function") {
compFunc = tempComp[tempComp.length - 1];
} else {
compFunc = tempComp;
}
// 10 parameter dependencies initialization for now
compFunc({}, {}, {}, {}, {}, {}, {}, {}, {}, {});
}
}
})();
I have now officially spent all day trying to assign a variable in JavaScript!
Forgive me for asking this same question 4 different ways, but here's what I started out with this morning, and this works. I just need to add a second method to it now.
Application = {};
(function() {
var closure = {};
Application.myFirstMethod = function() {
if (arguments.length) {
closure = arguments[0];
} else {
return closure;
}
}
})();
Application.myFirstMethod(3.14);
result = Application.myFirstMethod();
log(result);
So my question is: and please be patient with me, if I add mySecondMethod to Application, then how do I keep the value of arguments[0] without using the variable that is currently called closure?
How about this, it defines a function that takes a string and returns a getter/setter function. The string is used to indicate what property to get/set the value as in variables.
Demo
Application = {};
(function() {
var variables = {};
Application.myFirstMethod = makeGetterSetter('myFirst');
Application.mySecondMethod = makeGetterSetter('mySecond');
function makeGetterSetter(name) {
return function () {
if (arguments.length) {
variables[name] = arguments[0];
} else {
return variables[name];
}
};
}
})();
Application.myFirstMethod(4);
result1 = Application.myFirstMethod();
Application.mySecondMethod(5);
result2 = Application.mySecondMethod();
console.log(result1);
console.log(result2);
If you wanted to have a getter or setter with custom logic in it before either event then it would be easiest to just define them separately. Stick with the this[property] pattern to keep all your fields in one spot.
Application.myCustomMethod = function() {
if (arguments.length) {
// some logic
variables['custom'] = arguments[0];
} else {
// some logic
return variables['custom'];
}
}
It looks like you are searching for adding properties to objects, in the Prototype-Oriented Programming Language sense of the term; just use the "this" object, which stands for the current calling context, and which will be set to your Application object when calling the methods:
Application = {};
(function() {
Application.myFirstMethod = function() {
if (arguments.length) {
this.foo = arguments[0];
} else {
return this.foo;
}
};
Application.mySecondMethod = function() {
if (arguments.length) {
this.bar = arguments[0];
} else {
return this.bar;
}
};
})();
Application.myFirstMethod(3.14);
console.log(Application.myFirstMethod());
Application.mySecondMethod(2097);
console.log(Application.mySecondMethod());
console.log(Application.myFirstMethod());
Here's what I figured out. I probably need to use the word new somewhere though.
Application = {};
(function() {
Application.myFirstMethod = FirstMethod();
Application.mySecondMethod = SecondMethod();
function FirstMethod() {
var closure = {};
return function(myArgument) {
if (arguments.length) {
closure.result = arguments[0]; // myArgument
} else {
return closure.result;
}
}
}
function SecondMethod() {
var closure = {};
return function(myArgument) {
if (arguments.length) {
closure.result = arguments[0]; // myArgument
} else {
return closure.result;
}
}
}
})();
Application.myFirstMethod(3.14);
result = Application.myFirstMethod();
log(result);
Application.mySecondMethod(2013);
result = Application.mySecondMethod();
log(result);
I'm currently starting to work on existing project. We have global namespace object FOO and a helper to create a nice namespace:
var FOO = FOO || {};
//...
FOO.namespace = function (name) {
var parts = name.split('.'),
parent = FOO,
i;
// strip redundant leading global
if (parts[0] === "FOO") {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if (typeof parent[parts[i]] === "undefined") {
parent[parts[i]] = {};
}
parent = parent[parts[i]];
}
return parent;
};
Because there are many submodules spread out over different files being worked on from different people I noticed different patterns on declaring new submodules.
A: direct creation
FOO.modulename = {
someMethod: function (param) {
// ...
}
}
B: Local alias with direct assignment
var modulename = FOO.namespace('modulename');
modulename.someMethod = function (param) {
// ...
}
C: Objectliteral assignment to submodule
var modulename = FOO.namespace('modulename');
FOO.modulename = {
someMethod: function (param) {
// ...
}
}
Now, A is obviously not the right way but what about the other options (and I guess i will discover at least 3 more ways to do it). They all achive the same thing and all work, but which method is the most "correct" one?
Consider the following jasmine spec:
describe("something.act()", function() {
it("calls some function of my module", function() {
var mod = require('my_module');
spyOn(mod, "someFunction");
something.act();
expect(mod.someFunction).toHaveBeenCalled();
});
});
This is working perfectly fine. Something like this makes it green:
something.act = function() { require('my_module').someFunction(); };
Now have a look at this one:
describe("something.act()", function() {
it("calls the 'root' function of my module", function() {
var mod = require('my_module');
spyOn(mod); // jasmine needs a property name
// pointing to a function as param #2
// therefore, this call is not correct.
something.act();
expect(mod).toHaveBeenCalled(); // mod should be a spy
});
});
This is the code I'd like to test with this spec:
something.act = function() { require('my_module')(); };
This has bogged me down several times in the last few months. One theoretical solution would be to replace require() and return a spy created with createSpy(). BUT require() is an unstoppable beast: it is a different "copy" of the function in each and every source file/module. Stubbing it in the spec won't replace the real require() function in the "testee" source file.
An alternative is to add some fake modules to the load path, but it looks too complicated to me.
Any idea?
rewire is awesome for this
var rewire = require('rewire');
describe("something.act()", function() {
it("calls the 'root' function of my module", function() {
var mod = rewire('my_module');
var mockRootFunction = jasmine.createSpy('mockRootFunction');
var requireSpy = {
mockRequire: function() {
return mockRootFunction;
}
};
spyOn(requireSpy, 'mockRequire').andCallThrough();
origRequire = mod.__get__('require');
mod.__set__('require', requireSpy.mockRequire);
something.act();
expect(requireSpy.mockRequire).toHaveBeenCalledWith('my_module');
expect(mockRootFunction).toHaveBeenCalled();
mod.__set__('require', origRequire);
});
});
It looks like I found an acceptable solution.
The spec helper:
var moduleSpies = {};
var originalJsLoader = require.extensions['.js'];
spyOnModule = function spyOnModule(module) {
var path = require.resolve(module);
var spy = createSpy("spy on module \"" + module + "\"");
moduleSpies[path] = spy;
delete require.cache[path];
return spy;
};
require.extensions['.js'] = function (obj, path) {
if (moduleSpies[path])
obj.exports = moduleSpies[path];
else
return originalJsLoader(obj, path);
}
afterEach(function() {
for (var path in moduleSpies) {
delete moduleSpies[path];
}
});
The spec:
describe("something.act()", function() {
it("calls the 'root' function of my module", function() {
var mod = spyOnModule('my_module');
something.act();
expect(mod).toHaveBeenCalled(); // mod is a spy
});
});
This is not perfect but does the job quite well. It does not even mess with the testee source code, which is kind of a criterion for me.
I needed to do this today and came across this post. My solution follows:
In a spec helper:
var originalRequire = require;
var requireOverrides = {};
stubModule = function(name) {
var double = originalRequire(name);
double['double'] = name;
requireOverrides[name] = double;
return double;
}
require = function(name) {
if (requireOverrides[name]) {
return requireOverrides[name];
} else {
return originalRequire(name);
}
}
afterEach(function() {
requireOverrides = {};
});
In a spec:
AWS = stubModule('aws-sdk');
spyOn(AWS.S3, 'Client');
// do something
expect(AWS.S3.Client).toHaveBeenCalled();
This was very helpful, but it doesn't support calling through via .andCallThrough().
I was able to adapt it though, so I thought I'd share:
function clone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
var key;
var temp = new obj.constructor();
for (key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = clone(obj[key]);
}
}
return temp;
};
spyOnModule = function spyOnModule(name) {
var path = require.resolve(name);
var spy = createSpy("spy on module \"" + name + "\"");
moduleSpies[path] = spy;
// Fake calling through
spy.andCallThrough = function() {
// Create a module object
var mod = clone(module);
mod.parent = module;
mod.id = path;
mod.filename = path;
// Load it backdoor
originalJsLoader(mod, path);
// And set it's export as a faked call
return this.andCallFake(mod.exports);
}
delete require.cache[path];
return spy;
};
You can use gently module (https://github.com/felixge/node-gently). Hijacking require is mentioned in examples, and dirty NPM module actively uses it, so I suppose it works.
There is another approach. You can put the module in the global scope by not using var when requiring it:
someModule = require('someModule');
describe('whatever', function() {
it('does something', function() {
spyOn(global, 'someModule');
someFunctionThatShouldCallTheModule();
expect(someModule).toHaveBeenCalled();
}
}
You could also wrap the module in another module:
//someModuleWrapper.js
require('someModule');
function callModule(arg) {
someModule(arg);
}
exports.callModule = callModule;
//In the spec file:
someModuleWrapper = require('someModuleWrapper');
describe('whatever', function() {
it('does something', function() {
spyOn(someModuleWrapper, 'callModule');
someFunctionThatShouldCallTheModule();
expect(someModuleWrapper.callModule).toHaveBeenCalled();
}
}
And then obviously make sure that wherever someFunctionThatShouldCallTheModule is, you're requiring the wrapper rather than the real module.