Jasmine: testing method called from window scope function - javascript

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);

Related

Using Jasmine how to check if a local method has been called?

I am trying to test if a local method (method1) is being called from another method(method2). I tried something like this but it does not work as the method1() still has the original definition. The code snippet looks like this:
var ClassA = function () {
var method1 = function () {
console.log('method1');
};
var method2 = function () {
method1();
};
return {method1: method1, method2: method2}
}
Test case:
it("should call method1 when method2 is called", function () {
var objectA = new ClassA();
spyOn(objectA, 'method1').andCallThrough;
objectA.method2();
expect(objectA, 'method1').toHaveBeenCalled();
});
Tried overriding method1 to without any success:
objectA.method1 = jasmine.createSpy('aSpy').andCallThrough();
When you call new ClassA() you invoke the ClassA function and get a new object with the related prototype. This means that when you spyOn(ClassA, "doSomething") you're not setting up a spy on the object returned by the new call, but on a possible doSomething function on the ClassA function itself.
You should be able to do something like:
it("calls method1", function() {
var originalConstructor = ClassA,
spiedObj;
spyOn(window, 'ClassA').and.callFake(function() {
spiedObj = new originalConstructor();
spyOn(spiedObj, 'method1');
return spiedObj;
});
method2();
expect(spiedObj.method1).toHaveBeenCalled();
});
Please let me know if this works if not we can discuss more.

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() {
// ...
});
});

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();
});

Mocha testing: faking a method

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.

Javascript inheritance and method overriding

Assume I have a class like this:
function Widget() {
this.id = new Date().getTime();
// other fields
}
Widget.prototype = {
load: function(args) {
// do something
}
}
From this class I created some other classes which inherit the same prototype but have some added methods. What I want to do is being able to define a load() method in the sub-classes which first calls the parent method and then execute some code. Something like:
SpecialWidget.prototype = {
load: function(args) {
super.load(args);
// specific code here
}
}
I know there's no super keyword in Javascript but there must be a way to do this.
You can simulate it like this:
SpecialWidget.prototype = {
load: function(args) {
Widget.prototype.load.call(this, args);
// specific code here
}
}
Or you can create your own super property like this:
SpecialWidget.prototype.parent = Widget.prototype;
SpecialWidget.prototype = {
load: function(args) {
this.parent.load.call(this,args);
// specific code here
}
}
so first, you set up your 'subclass' like so
function SubClass(name) {
Super.call(this);
// stuff here
}
SubClass.prototype = new SuperClass(null);
SubClass.prototype.constructor = SubClass;
and then you can do
SuperClass.prototype.theMethod.apply(this);
from within a subclass implementation to specifically invoke the super's implementation.
I don't know if this is the best solution, but you could do something like this:
function Widget() {
this.id = new Date().getTime();
}
Widget.prototype.load = function(args) {
alert( 'parent load' );
};
SpecialWidget = function(){};
// Make the prototype of SpecialWidget an instance of Widget
var proto = SpecialWidget.prototype = new Widget;
// Give the prototype a function that references the "load" from Widget
proto.parent_load = proto.load;
// Give SpecialWidget its own "load" that first calls the parent_load
proto.load = function( args ) {
this.parent_load( args );
alert( 'special load' );
};
var inst = new SpecialWidget;
inst.load();
This makes the prototype of SpecialWidget an instance of Widget so that it inherits all that Widget has.
Then it makes a reference to the load() of Widget called parent_load(), and creates its own load() that calls the parent_load() when invoked.
Since mid-2015 (ECMAScript 2015), javascript has Classes and super
Here's the link: https://262.ecma-international.org/6.0/, see section 12.3.5 (super) and 14.5 (Class definitions).
How your code would look with those changes:
class Widget() {
constructor() {
this.id = new Date().getTime();
// other fields
}
load(args) {
// do something
}
}
class SpecialWidget extends Widget {
load(args) {
super.load(args);
// specific code here
}
}
The closest I got to the previous syntax (without using class but using super) was using Object.setPrototypeOf:
// UNCHANGED
function Widget() {
this.id = new Date().getTime();
// other fields
}
Widget.prototype = {
load: function(args) {
// do something
}
}
// slightly changed to declare SpecialWidget
function SpecialWidget() {}
// changed to define load as an method, and not a property with function as value
SpecialWidget.prototype = {
load(args) {
super.load(args);
// specific code here
}
}
// here's the key
Object.setPrototypeOf(SpecialWidget.prototype, Widget.prototype);
The declaration of load was changed because super can be used inside methods, but not functions. So, instead of load: function(args) { body }, it's simply load(args) { body }.
But, there's a caveat: with this solution, elements of SpecialWidget will not inherit the id defined as new Date().getTime(). I don't think there's a workahound (without using classes or duplicating code declaring this.id inside SpecialWidget).
It would be possible to store the old value of the load method in a closure, if you did your overriding like this:
function Widget() {
this.id = new Date().getTime();
// other fields
}
Widget.prototype = {
load: function(args) {
// do something
alert("Widget Prototype Load");
}
};
function SpecialWidget(){
};
SpecialWidget.prototype = new Widget();
(function(){
var oldLoad = SpecialWidget.prototype.load;
SpecialWidget.prototype.load = function(){
oldLoad();
alert("new Load");
};
}());
var x = new SpecialWidget();
x.load();
It works, but I'm not sure if it's the best method.
Using Simple Javascript Class:
Class.extend('Widget', {
load: function () {
alert('foo');
}
});
Widget.extend('SpecialWidget', {
load: function () {
this.super();
alert('bar');
}
});
new Widget().load(); // Alert: 'foo'
new SpecialWidget().load(); // Alert: 'foo' and 'bar'
Take a look at Simple Javascript Class Project, Simple JavaScript Inheritance and Inheritance Patterns in JavaScript.

Categories

Resources