I want to test whether the following method is called with in my Javascript object constructor. From what I have seen in the Jasmine documentation, I can spy on a constructor method and I can spy on methods after an object has been instantiated, but I can't seem to be able to spy on a method before the object is constructed.
The object:
Klass = function() {
this.called_method();
};
Klass.prototype.called_method = function() {
//method to be called in the constructor.
}
I want to do something like this in the spec:
it('should spy on a method call within the constructor', function() {
spyOn(window, 'Klass');
var obj = new Klass();
expect(window.Klass.called_method).toHaveBeenCalled();
});
Spy directly on the prototype method:
describe("The Klass constructor", function() {
it("should call its prototype's called_method", function() {
spyOn(Klass.prototype, 'called_method'); //.andCallThrough();
var k = new Klass();
expect(Klass.prototype.called_method).toHaveBeenCalled();
});
});
Broadly, I agree with Dave Newton's answer above. However, there are some edge-cases to this approach that you should consider.
Take a variation to Dave's solution, with another test-case:
// production code
var Klass = function() {
this.call_count = 0;
this.called_method();
};
Klass.prototype.called_method = function() {
++this.call_count;
};
// test code
describe("The Klass constructor", function() {
it("should call its prototype's called_method", function() {
spyOn(Klass.prototype, 'called_method');
var k = new Klass();
expect(k.called_method).toHaveBeenCalled();
});
it('some other test', function() {
var k = new Klass();
expect(k.call_count).toEqual(1);
});
});
The second test will fail because the spy setup in the first test persists across the test boundaries into the second method; called_method doesn't increment call_count, so this.call_count does not equal 1. It's also possible to come up with scenarios with false positives - tests that pass, that shouldn't.
On top of this, because the spy remains, the more Klass instances that are created, the bigger the memory heap the spy will consume, because the spy will record each call to called_method. This probably isn't a problem in most circumstances, but you should be aware of it, just in case.
A simple solution to this problem would be to make sure that the spy is removed after it has been used. It can look a bit ugly, but something like this works:
// test code
describe("The Klass constructor", function() {
it("should call its prototype's called_method", function() {
var spy = jasmine.createSpy('called_method');
var method = Klass.prototype.called_method;
Klass.prototype.called_method = spy;
var k = new Klass();
expect(spy).toHaveBeenCalled();
Klass.prototype.called_method = method;
});
[NOTE - a little opinion to finish] A better solution would be to change the way you write production code to make the code easier to test. As a rule, spying on prototypes is probably a code-smell to be avoided. Instead of instantiating dependencies in the constructor, inject them. Instead of doing initialization in the constructor, defer to an appropriate init method.
Related
Please don't suggest to use Sinon. I want to get chai-spies specifically chai.spy.on working with your help. Basically, I have this spec. Inside my initialize method in PatientController, I call this.initializePatientEvents();
beforeEach(function() {
this.patientController = new PatientController({model: new Backbone.Model(PatientModel)});
});
it('executes this.initializePatientEvents', function () {
let spy = chai.spy.on(this.patientController, 'initializePatientEvents');
expect(spy).to.have.been.called();
});
However, the test is failing with this error
AssertionError: expected { Spy } to have been called
at Context.<anonymous>
I spent almost 3 hours now with no luck! :(
Moving my comment above to a response here:
Looking at your code, I'm just not sure what the this reference refers to. And based on your error message, it seems like its related to something about the context. Therefore, I'd try something like this:
var patientController;
beforeEach(function() {
patientController = new PatientController({model: new Backbone.Model(PatientModel)});
});
it('executes this.initializePatientEvents', function () {
let spy = chai.spy.on(patientController, 'initializePatientEvents');
expect(spy).to.have.been.called();
});
If this doesn't work, then its more specific to your implementation of patientController and the initializePatientEvents method, and not something related to chai.spy.
EDIT:
Here's something I set up locally and I was able to get a passing test. The main difference is that instead of using Backbone, I just created my own constructor function.
"use strict";
var chai = require("chai");
var sinon = require("sinon");
var sinonChai = require("sinon-chai");
chai.use(sinonChai);
var expect = chai.expect;
var should = chai.should();
describe("PatientController Test", function() {
var PatientController;
var initializePatientEventsSpy;
var patient;
beforeEach(function() {
PatientController = function(name, age) {
this.name = name;
this.age = age;
this.initializePatientEvents();
};
PatientController.prototype.initializePatientEvents = function() {
console.log("Do some initialization stuff here");
};
initializePatientEventsSpy = sinon.spy(PatientController.prototype, "initializePatientEvents");
});
it("should test initializePatientEvents was called", function() {
patient = new PatientController("Willson", 30);
initializePatientEventsSpy.should.have.been.called;
});
});
If the PatientController constructor is what calls initializePatientEvents, then the timing of your spy creation is a bit off. Currently, the order of your spy-function relationship is:
Call function
Spy on function
Expect spied on function to have ben called
Because the function is not being spied on when it is called, the spy misses the call entirely. What the order should be is:
Spy on function
Call function
Expect spied on function to have ben called
However, you are in the sticky situation where the object you are spying on doesn't exist until after the constructor is called. One workaround would be to assert that the effects of the initializePatientEvents have taken place instead of asserting that the function was called.
I have a function
var data = {};
var myFunc = function() {
data.stuff = new ClassName().doA().doB().doC();
};
I'd like to test that doA, doB, and doC were all called.
I tried spying on the instance methods like this
beforeEach(function() {
spyOn(ClassName, 'doA');
};
it('should call doA', function() {
myFunc();
expect(ClassName.doA).toHaveBeenCalled();
});
but that just gives me a "doA() method does not exist" error.
Any ideas?
Where you went wrong was your understanding of how to refer to methods in JavaScript in a static context. What your code is actually doing is spying on ClassName.doA (that is, the function attached to the ClassName constructor as the property doA, which is not what you want).
If you want to detect when that method gets called on any instance of ClassName anywhere, you need to spy on the prototype.
beforeEach(function() {
spyOn(ClassName.prototype, 'doA');
});
it('should call doA', function() {
myFunc();
expect(ClassName.prototype.doA).toHaveBeenCalled();
});
Of course, this is assuming that doA lives in the prototype chain. If it's an own-property, then there is no technique that you can use without being able to refer to the anonymous object in myFunc. If you had access to the ClassName instance inside myFunc, that would be ideal, since you could just spyOn that object directly.
P.S. You should really put "Jasmine" in the title.
Let’s do some code refactoring as we want implement constructor injection pattern as James Shore mentions that:
Dependency injection means giving an object its own instance variables. Really. That’s it.
var data = {};
var stuff = new ClassName()
var myFunc = function(stuff) { // move step of creation new object outside
data.stuff = stuff.doA().doB().doC();
};
And time for some tests
function ClassName() {
}
var data = {};
var stuff = new ClassName()
var myFunc = function(stuff) {
data.stuff = stuff.doA().doB().doC();
};
describe('stub for ClassName implementation', function() {
var stubStuff = {
doA: function() {
return stubStuff
},
doB: function() {
return stubStuff
},
doC: function() {
return stubStuff
}
}
beforeEach(function() {
spyOn(stubStuff, 'doA').and.callThrough();
});
it('calls "doA" on "myFunc" exection', function() {
myFunc(stubStuff);
expect(stubStuff.doA).toHaveBeenCalled();
});
});
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
I was writing jasmine test today and had to mock an knockout.js object result and to my surprise they were not equal although objects looked identical. After eliminating properties I narrowed it down to knockout.js observable. Below are two tests knockout one is failing while the one with function is successful.
(function() {
var describe = window.describe,
it = window.it,
expect = window.expect;
describe("Compare objects that have functions", function() {
function AddIsExpandedProperty(recordObject) {
recordObject.IsExpanded = ko.observable(false);
return recordObject;
}
it("object that has knockout function", function() { // this fails
var recordX = AddIsExpandedProperty({});
expect(recordX).toEqual(AddIsExpandedProperty({}));
});
it("object that has normal function", function() { // this succeeds
var func = function () { };
var recordY = { f: func };
expect(recordY).toEqual({ f: func });
});
});
})();
I was wondering why? Isin't knockout.js observables are mere functions?
One solution to this problem would be to create unwrapper that would be to unwraping all knockout.js observables and producing the unwraped objects. Then one could compare them to the mocked ones, but this would not be real a unit testing.
Other would be to use .toBeTruthy() instead of .toEqual() but this again is jeopardising the test - if those functions would be different test would pass.
What solutions are there that would not require object modifications?
I have also made a miserable attempt to write jasmine test mock on plunker.
It is natural that the two object returned from AddIsExpandedProperty are not equal.
The ko.observable function itself returns a new function instance every time you call it. So you can think about ko.observable as a constructor function so when checking strict equality two ko.observable() call never return the same object.
So your counter example is not testing how KO behaves, a more accurete example would be if your func returns a new function:
it("object that has normal function", function() {
var func = function () { return function() {} };
var recordY = { f: func() };
expect(recordY).toEqual({ f: func() });
});
Of course this test case ialso fails like the KO one.
If you want to test object equality with ko.observable properties then I would suggest to use the ko.toJS to turn your KO observables to regular properties:
it("object that has knockout function with toJS", function() {
var recordX = AddIsExpandedProperty({});
expect(ko.toJS(recordX)).toEqual(ko.toJS(AddIsExpandedProperty({})));
});
Demo Plnkr.
I need to edit the function which locates inside of the constructor.
Example:
some.thing = function() {
this.somefn = function() { // this is the function that I need to fix
...
}
}
But function should be edited not just only for a single object (new obj = some.thing();) but also for any created objects by this constructor.
So is there any way to edit such inner-functions?
Here is a solution based on prototype:
var Something = function () {
this.f = function () {
console.log("Something");
};
};
var Old = Something;
var Something = function () {
Old.apply(this);
this.f = function () {
console.log("New");
};
};
Something.prototype = new Old();
var s = new Something();
s.f(); // prints "New"
The solutions seem just a little too obvious, so I'm wondering if the trouble is that you don't have access to the original code, and you need a more dynamic solution.
If so, one option may be to override the constructor with your own constructor, and have it call the original, and then update the object.
Original code:
some.thing = function() {
this.somefn = function() { // this is the function that I need to fix
...
}
}
Your code:
// cache a reference to the original constructor
var _thing = some.thing;
// your constructor
some.thing = function() {
// invoke the original constructor on the new object.
_thing.apply(this, arguments);
this.somefn = function() { /*your updated function*/ };
};
// maintain inheritance
some.thing.prototype = Object.create(some.thing.prototype);
// make an instance
var theThing = new some.thing();
Now you're getting the benefit of the original constructor and prototype chain, but you're injecting your own function on to the objects being created.
Only trouble may be that the original function you replaced could make special use of the original constructor's variable scope. If that's the case, there would be an issue to resolve.
It would be possible to retain and invoke the original method that you overwrote before invoking yours. Not sure if this situation calls for that or not.
I exactly know your need cause last week I passed through it. I just implemented a complete inheritance model in javascript and as far as I remember, I had a problem with overriding constructors and calling the parent class's ctor when child class is initializing.
So I just solved the problem with modifing some points in my design and it's now working like a charm! (something like C# but in Javascript)
By the way, I don't suggest you to change a method contents this way, but here is a way to do that (I myself did not do that this way and AGIAIN I DO NOT RECOMMEND IT. THERE ARE MANY OTHER WAYS, BUT THIS IS THE EASIEST):
var test = function() { /*InjectionPlace*/ };
eval("var newTest = " + test.toString().replace(
"/*InjectionPlace*/",
"var i = 10; alert(i);"
));
test();
newTest();
Cheers
I'm trying to do the following:
//Code under test
function Foo() {
this.do_something_interesting = function() {
var dependency = new CanYouMockMe();
if(dependency.i_want_stubbed() === true) {
//do stuff based on condition
} else {
//do stuff if false
}
}
}
//Test Code
describe("Foo", function () {
it("should do something if the dependency returns true", function () {
var foo = new Foo();
//how do I stub and/or redefine the "i_want_stubbed" method here?
var result_if_true = foo.do_something_interesting();
expect(true).toEqual(result_if_true);
});
});
The gist of the question is: how do I redefine an instance method in javascript?
Your Foo.do_something_interesting demonstrates a common feature of untestable / hard-to-test code, namely that it uses "new" and has a dependency that is not passed-in. Ideally, you would have:
do_something_interesting = function(dependency) {
// ...
}
In the above, it is much easier to replace your dependency with a Mock. That said, you can use the properties of a given instance or of the prototype to replace bits and pieces. For example:
Foo.prototype.CanYouMockMe = function() {};
Foo.prototype.CanYouMockMe.prototype.i_want_stubbed = function() {
console.log("I'm a stub");
};
You can save the properties before you overwrite them, and then restore those properties after your test case, to make it possible to run multiple tests in isolation of each other. That said, making dependencies explicit is a big win both for testability and for making your APIs more flexible / configurable.