I'm trying to test code using Sinon.js, but I'm unfamiliar with out it's supposed to behave.
I expect that I can create a 'fake' object, wrap it with sinon and pass it to whatever I'm testing, and have it do its thing. However, it seems like every time I try to wrap a sinon object, the function is not there:
var event_api = {
startTime: function() {
return '123';
}
}
var stub = sinon.stub(event_api);
console.log(stub.startTime()) // returns undefined
var mock = sinon.mock(event_api);
console.log(mock.startTime()) // returns undefined
What am I missing?
It depends on what are you trying to do:
If you don't have any expectations on the call then you should use a stub, for example startTime() only has to return a value.
var event_api = {
startTime: sinon.stub().returns('123')
}
console.log(event_api.startTime());
But if what you want is to set some assertions for the call, then you should use a mock.
var event_api = {
startTime: function() {
return '123';
}
}
//code to test
function getStartTime(e) {
return e.startTime();
}
var mock = sinon.mock(event_api);
mock.expects("startTime").once();
getStartTime(event_api);
mock.verify();
Hope this helps.
The function is indeed there, but it's void of any functionality, since it has been stubbed.
If you want to log the function itself in the console, you have to execute:
console.log(stub.startTime) //logs the function itself
instead of:
console.log(stub.startTime()) //logs the result of the function, which is undefined
However, as said, all the methods of a stub object have been "emptied" of their functionality. If you want to make a method of a stubbed object return a value, you can do the following:
var stub = sinon.stub(event_api);
stub.startTime.returns(123);
console.log(stub.startTime) //log the function
console.log(stub.startTime()) //log the result of function, that is now 123
Related
Hello I got a question regarding mocking JS code with Jasmine.
Imagine having the following situation:
function Test(){
var a = 5;
var b = 3;
Test2(a,b);
}
function Test2(a,b){
var result = a + b;
console.log("result of function Test2: ", result);
}
I want to mock my Test2 function call with Jasmine. I tried the following thing:
describe("1# Test Mocking", function () {
it("test: Mocking Example", function () {
var myMock = new Test();
spyOn(myMock, "Test2").and.returnValue(10,10);
expect(Test2.result).toEqual(20);
});
});
But Jasmine keeps saying: Error: Test2() method does not exist
Does anyone knows why this is and how I could solve this?
Your code doesn't make a lot of sense I'm afraid:
you're telling Jasmine to spy on a method of myMock called Test2, yet it doesn't have such a method (as Jasmine is telling you); Test2 is just a regular function;
even if it did had a method called Test2, you're spying on it after new Test(), at which point the original Test2 would already have been called and the spy would be declared too late;
the original Test2 doesn't return a value, yet you are telling the spy that it should return 10, 10 (it seems to me that you want to call it with those two values, not have it return them);
Test2 doesn't have any side-effects (like returning a value or setting an instance variable or something) apart from creating a local variable (result) and logging it, which makes it pretty untestable;
I think you need to go back to the drawing board to formulate what exactly it is you want the class to do. To help you on your way, here's a possible implementation, including a test to see a) if Test2 gets called with the proper arguments and b) if its return value gets stored properly (again, I don't know what you want the class to do, so I'm just providing some examples):
function Test() {
var a = 5;
var b = 3;
this.result = this.Test2(a, b);
}
Test.prototype.Test2 = function(a, b) {
var result = a + b;
return result;
}
describe("1# Test Mocking", function () {
it("test: Mocking Example", function () {
spyOn(Test.prototype, 'Test2').and.returnValue(20);
var myMock = new Test();
expect(myMock.Test2.calls.argsFor(0)).toEqual([ 5, 3 ]);
expect(myMock.result).toEqual(20);
});
});
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.
Assume I have a simple object in js with one private variable:
function test(){
var value=true;
}
and now I want to create one instance:
var r=new test() //I want to get r === true
How can I return a value from it?
If I write:
function test(){
var value=true;
return value;
}
I have a test {} in result.
If I write:
function test(){
var value=true;
return function(){ return value; }
}
then I can get the value, but I must add additional parentheses:
var r=new test()() //r === true
I don't want the parentheses, so I tried to change the code to:
function test(){
var value=true;
return (function(){ return value; } )();
}
But in response, again I get test {}
How to write the return statement in this situation?
I believe you need to do something like:
function test(){
this.value = true;
}
and then
var r=new test();
if (r.value == true) {
//Do something
}
First I feel obliged to clarify a possible misunderstanding:
function test(){
var value=true;
}
is not an object with a private variable. It is a function with a local variable. When you call the function with new, it creates an object inheriting from the functions's prototype with no properties. If you call the function normally, it simply executes the function body and returns undefined (since you are not returning anything).
Solutions:
Do you actually need a constructor function? I'm asking because your example is very simple. Obviously you cannot have the function return two values, true and the object.
So, you could just call the function without new:
function test() {
var value = true;
return value;
}
var r = test();
If you really want r to be true then I see no reason to call the function as a constructor function.
The reason why you got test {} as result was because you called the function with new. If you do that, the function will always return an object and if you don't do so explicitly (value is a boolean, not an object), it implicitly returns this (which is an object).
So again, if you really want r to be equal to value from inside the function, then simply don't call the function with new.
If you need an object though, there are a couple of ways:
You can assign the value to a property and access it instead, like PokeHerOne showed in his answer or add a function which returns that value, as papaiatis demonstrates. The advantage is that the value is accessed explicitly and other people looking at your code understand what's going on.
Additionally, depending on what you want to do with that value / object, you can implement the valueOf methods, which gets called by various operators.
For example:
function Test(){
var value = true;
this.valueOf = function() {
return value;
}
}
var t = new Test();
console.log(t); // logs the Test instance
console.log(t == true); // logs `true`
I.e. t is an object but behaves like the value true (value) in various operations. This is powerful but can also be quite confusing, since the type conversion is somewhat implicit and it's not something that is used in JavaScript very often.
Used methods defined internally:
function TestClass(){
var value = true;
this.getValue = function(){
return value;
};
}
var t = new TestClass();
alert(t.getValue()); // true
Since value is defined as private it is not accessible from outside:
alert(t.value) // undefined
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.
I'm still trying to understand javascript scoping. What is the simplest way to return a value from an anonymous callback function? For instance,
var test = 'outside';
callsFunction(function() { test = 'inside'; });
console.log(test);
where callsFunction calls the anonymous function. I would like this to print 'inside'.
I'm a little bit confused, but I believe this is what you're after
function callsFunction( fn )
{
fn();
}
var test = 'outside';
callsFunction(function() { test = 'inside'; });
console.log(test);
Note that any function can be invoked via the usage of (). However, there are special methods for invoking functions from different contexts, such as Function.call() and Function.apply()
At the risk of pointing out the obvious, the simplest way to have something done after a callback is to simply include it in the callback (and this is, in fact, the way to go):
var test = 'outside';
callsFunction(function() {
test = 'inside';
console.log(test);
});
A real example: let's say you want to update a div with the contents of a file from an Ajax call.
Ajax('file.html', function (data) {
// Update the div here
document.getElementById('d').innerHTML = data;
});
// Not here
Maybe what you are wanting to research is closures in JavaScript?
You didn't define callsFunction in your question, but if you make it a self-invoking anonymous function, you can return the new 'test' value:
var test = 'outside';
(function() { test = 'inside'; })();
console.log(test); // returns 'inside'
If you do define callsFunction:
var test = 'outside';
var callsFunction = function() {
test = 'inside';
return test;
};
console.log(callsFunction()); // returns 'inside'
You can get more complicated with closures.