Mocha testing: faking a method - javascript

Problem
I have the following javascript file I want to test:
function myLocalHouse() {
this.init = function() {
$.post(//Ajax call using jQuery);
};
this.buyHouse(money, date) {
//code I want to test with mocha
};
this.init();
};
As you can see, the init method uses jQuery, and when I execute mocha in a console, to test it, fails as it doesn't find the $ object.
So my solution was to override and fake the init method. The problem is that my attemps to do that have failed miserably. So sad.
What I have tried
This is my test that tries to test myLocalHouse and my three attemps to override the init method to use an empty function instead.
var myLocalHouse = require('./myLocalHouse.js').myLocalHouse;
suite('houses suite', function() {
test('test that buy House works correctly', function() {
//First attemp at overriding init method: FAILED
myLocalHouse.prototype.init = function() {};
//Second attemp at overriding init method: FAILED
myLocalHouse.__proto__.init = function() {};
var myLocalHouseInstance = new myLocalHouse();
//Third attemp at overriding init method: FAILED
myLocalHouseInstance.prototype.init = function() {};
var something = myLocalHouseInstance.buyHouse(100, '17/08/2013');
});
});
I just...I don't know how to overriding the init method.
I'm doing something wrong? Is there another way to do the same thing?

If you don't want to modify anything at all in the legacy code, the following method can be used:
function myTestHouse() {
Object.defineProperty(this, 'init', {
set: function() { /* Ignore setter */ },
get: function() { return function() { /* dummy*/}; },
enumerable: true
});
var _this = myLocalHouse.apply(this, arguments);
return _this || this;
};
myTestHouse.prototype = myLocalHouse.prototype;
// Usage
var myLocalHouseInstance = new myTestHouse();
Using Object.defineProperty, I define a read-only method that silently ignores setters. After that, I call the original constructor (myLocalTestHouse) using apply.
Note: This method is fully transparent. The created instance behaves as if you were invoking the original constructor.

Related

Mocking/spying on a constructor "x.Thing()"

Assuming x is an external library and Thing is an object that can be constructed from x. This is all wrapped in an Angular service, like so:
app.service('thingService', function() {
var thing;
this.createThing = function(thingParam){
thing = new x.Thing(thingParam);
}
});
My initial attempt included this:
xSpy = jasmine.createSpyObj('x', ['Thing']);
spyOn(window, 'x').andReturn('xSpy');
But it still complains that x() method does not exist on that line that Thing should be constructed
Your attempt
xSpy = jasmine.createSpyObj('x', ['Thing']);
spyOn(window, 'x').andReturn('xSpy');
is wrong:
spyOn() replaces methods with spies, since x is an object this won't work. This is why you get the exception x() method does not exist.
Assuming your example you can just replace the attribute:
describe("Test", function() {
var origThing;
beforeEach(function() {
// create spy object for Thing that provides its methods
var mockedThingInterface = jasmine.createSpyObj('Thing', ['methodA', 'methodB']);
mockedThingInterface.methodA.and.returnValue(1);
mockedThingInterface.methodB.and.returnValue(2);
// remember original constructor
origThing = x.Thing;
// replace the constructor
x.Thing = function() {
return mockedThingInterface;
}
});
afterEach(function() {
// restore when finished
x.Thing = origThing;
});
it("should ...", function() {
// ...
});
});

How to redefine private method in javascript?

I create component from Trumbowyg plugin to vue.js library. I need add two way binding in this beautiful wysiwyg editor.
How to rewrite buildEditor() method?
This method is private. how to do it correctly?
<script>
jQuery.trumbowyg = {
// options object
};
(function (navigator, window, document, $, undefined) {
$.fn.trumbowyg = function (options, params) {
// ... code ...
$(this).data('trumbowyg', new Trumbowyg(this, options));
// ... code ...
};
var Trumbowyg = function (editorElem, o) {
var t = this;
// ... code ...
t.init();
};
Trumbowyg.prototype = {
init: function () {
var t = this;
t.buildEditor();
// ... code ...
},
buildEditor: function () {
// i need rewrite this method
}
// code for otner method
};
})(navigator, window, document, jQuery);
// -------------------------------------
// other file. I want init this plugin
// here do need to rewrite buildEditor() method? What best way to do this?
$('.selector').trumbowyg();
If this plugin doesn't return 'Trumbowyg' variable it's not possible. I recommend fork this plugin and create your own version with your settings.
The best way to do it would be to fork the plugin yourself, as Slava answered. But technically you're able to modify that function.
Whenever you construct a class, that instance has its own constructor property. This is equal to the class function.
So, if you can get access to an instance of Trumbowyg, you're able to use its class:
$foo.trumbowyg(...)
var trumbowyg = $foo.data('trumbowyg')
var TrumbowygClass = trumbowyg.constructor
Now we can modify its prototype:
TrumbowygClass.prototype.buildEditor = function() {
// ...
}
You might want to make $foo be a temporary or unused element. That's because it'll have called the old buildEditor (as soon as you ran $foo.trumbowyg()), not your own modified version.
After you've set the prototype function you could run it on the element you actually want to use trumbowyg on (e.g. $('#target'))
As an example:
(function() {
window.makeInstance = function() {
return new HiddenClass()
}
var HiddenClass = function() {
this.setGreeting()
this.showGreeting()
}
HiddenClass.prototype.setGreeting = function() {
this.greeting = 'Hello, world!'
}
HiddenClass.prototype.showGreeting = function() {
console.log(this.greeting)
}
})()
var myTempInstance = makeInstance()
// Should log 'Hello, world!'
var HiddenClass = myTempInstance.constructor
console.log(HiddenClass) // Should be the HiddenClass function
// Now we overwrite our function..
HiddenClass.prototype.setGreeting = function() {
this.greeting = 'Redefined!'
}
var myUsedInstance = makeInstance()
// Should log 'Redefined!', since we redefined setGreeting
// And later we can use `myUsedInstance`.
// In this code myTempInstance is like $foo, and myUsedInstance
// is like $('#target').

Jasmine: testing method called from window scope function

I'm using Jasmine to te test some of my code. It looks a bit like this
# main logic
function Analytics() {
this.construct = function() {
}
this.foo = function() {
}
this.bar = function() {
}
}
# "main" routine, called by jQuery on ready, or direct by Jasmine
function analytics() {
new Analytics().construct();
}
# calls main routine
$(document).ready(function () {
analytics();
});
When running this in the browser, it works fine. However, when I want to test my code with Jasmine (test if the constructor gets called when calling analytics() it fails.
Expected spy construct to have been called. (1)
This is what the spec looks like:
it('should call the constructor when the document is ready', function({
var _analytics = new Analytics();
spyOn(_analytics, 'construct')
analytics(); # note this is the "main" routine
expect(_analytics.construct).toHaveBeenCalled();
})
My testcase seems to be incorrect but I don't really see how. Does anyone have an explanation for this behavior?
As I see, from code "analytics" function creates new instance of Analytics.
So probably test works like that:
it('should call the constructor when the document is ready', function({
var _analytics = new Analytics(); // --> create new instance of Analytics
spyOn(_analytics, 'construct') // --> spy on construct func
analytics(); // --> This, creates new instance
// of analytics which you don't spy on.
expect(_analytics.construct).toHaveBeenCalled();
});
Try to spy via prototype:
spyOn(Analytics.prototype, 'construct'); // will spy all instances.
And test will look like this:
it('should call the constructor when the document is ready', function({
spyOn(Analytics.prototype, 'construct');
analytics();
expect(Analytics.prototype.construct).toHaveBeenCalled();
});
Note that you don't have access to instance created in analytics function.
You will be not able to use it after creation.
I don't know the context of the task. But maybe you should use default constructor.
function Analytics(options) {
// this is constructor
this.prop1 = options.prop1;
this.prop2 = options.prop2;
this.foo = function() {
}
this.bar = function() {
}
}
var analyticsModule = new Analytics(options);

Getting "Undefined is not a function" error when using the Revealing Prototype Pattern

I'm trying to employ the Revealing Prototype Pattern in a JavaScript file to encapsulate two collections of related functions. But when the page loads, it returns the following error at the call to the .init function:
"Uncaught TypeError: Undefined is not a function."
Here is the pattern for my markup.
<script>
$(function () {
testProto1.init();
testProto2.init();
});
</script>
And here is the pattern in my JavaScript file.
var testProto1 = function () {
};
testProto1.prototype = function () {
var init = function () {
alert("init 1");
};
return {
init: init
}
}();
var testProto2 = function () {
};
testProto2.prototype = function () {
var init = function () {
alert("init 2");
};
return {
init: init
}
}();
This is probably some basic syntax error on my part, and I do apologize if it's a duplicate. Why am I seeing this error and how do I fix it? Thanks.
It looks like you're using the concepts of prototypes & function instances incorrectly in a lot of ways.
You need to instantiate a function with the new operator if you want to be able to access prototypes.
From what it looks like you're trying to achieve this:
var testProto1 = function () { };
// Create your methods in object notation within your prototype
testProto1.prototype.init = function () {
alert('init called');
};
Now if you want to call this, you have to instantiate it!
var proto1 = new testProto1();
// NOW you can call .init! Because the prototype was actually created
proto1.init(); // alerts 'init called!'
you can access prototype's properties from instances of this Object, so this will work:
var a=new testProto1();
a.init();
if you want to acces init function from testProto1 you must write:
testProto1.prototype.init();
so your code will look like:
$(function () {
testProto1.prototype.init();
testProto2.prototype.init();
});

Invoke javascript function from string

I have the following code in my javascript module, however this requires me to make the functions visible to the outside world.
var mymodule = function() {
var self = null,
init = function () {
self = this;
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
},
send = function () {
// some logic
},
finish = function () {
// some logic
},
delete = function () {
// some logic
};
return {
init: init,
send: send,
finish: finish,
delete: delete
};
}();
mymodule.init();
So the only thing I want to return in my module is the init function. However when I do this I cant invoke the functions, because the object (self) only contains the init function visible on the outside.
return {
init: init
};
Is there any solution to invoke my functions like this without making them visible to the outside world? Please no if else statements, because my workflow is bigger then the 3 actions in this example. I want to make my module as closed as possible because this reduces the dependencies.
Update
Here is a updated jsfiddle with one of the proposed solutions, however this is giving me another issue. http://jsfiddle.net/marcofranssen/bU2Ke/
Something like this would work:
var mymodule = function() {
var self = this;
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'), action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
}
self.send = function () {
console.log('send');
}
self.finish = function () {
console.log('finish');
}
self.delete = function (item) {
console.log('delete');
};
return {
init: init,
};
}();
mymodule.init();​
Here's the fiddle:
http://jsfiddle.net/yngvebn/SRqN3/
By setting the self-variable to this, outside the init-function, and attaching the send, finish and delete functions to self, you can use the self[action] syntax from within the init-function
Yes, there is an easy (but perhaps slightly messy) way you can do this without making the functions visible to the global object:
var privateFunctions = { deleter: deleter, send: send};
Then, instead of self[action]();, just do privateFunctions[action](); and you're good to go.
Note that I changed delete to deleter, because delete is a reserved keyword...
var mymodule = function() {
var self = {},
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
};
self.send = function () {
// some logic
};
self.finish = function () {
// some logic
};
self.delete = function () {
// some logic
};
return{
init:init
}
}();
mymodule.init();
This should Work!!
Even if you return an object just with the init property and you populate the rest dynamically such that your module uses them, you would still be making them visible to the outside at runtime. Anyone who wants to debug your module would easily get to them.
You can still create anonymous methods at runtime and they would also be visible together with their implementation.
In your code example, it is vague what "self" really is. You should keep it simple, use encapsulated functions as "private" methods and return a "public" (or "privileged" as Crockford calls it) function that have access to them.
This is the YUI way of doing singletons with private functions and variables. Example pattern:
var mymodule = (function() {
var internal = {
'send': function() {},
'finish': function() {},
'delete': function() {}
};
return {
'init': function(action) {
// access to internals, f.ex:
if ( internal.hasOwnProperty(action) ) {
internal[action].call(this); // bring the caller context
}
}
};
}());
mymodule.init('send');

Categories

Resources