var obj = {};
obj.localContext = 'firstTemp';
obj.call = function(){
obj.localContext = 'secondTemp';
};
Jasmine-spec:
it('value of localContext', function(){
spyOn(obj, 'call');
obj.call();
expect(obj.localContext).toEqual('secondTemp');
});
Why is the obj.call() method never being called? When I run the spec, the value of obj.localContext is still firstTemp instead of secondTemp
When you create a spy, the default behaviour is to replace the object with a mock that doesn't call the original. Typically you'd use it to test functionality that would otherwise call out to other APIs you don't want to be called - you can test that they would have been called, without actually calling them.
Jasmine does provide you a way to also call the original function though:
spyOn(obj, "call").and.callThrough();
See the Jasmine documentation for spies (unfortunately, linking directly to the and.callThrough section doesn't work)
Related
When I wrap the method of a class into a Sinon-spy like this:
sinon.spy(myObject, "myMethod")
What happens within the spy?
I guess the spy-object has a reference which points to "myObject.myMethod".
What happens when the method becomes call?
I know that the spy logs information about the invocation like times of invocations, the used parameter etc.
But does the myMethod really become invoked?
I mean: Passes the spy object the invocation further? Does the spy-object act as proxy? Or does it only log the information?
From a simple test it seems that sinon spy does call the original method:
it('does a thing', function() {
const el = {};
el.thing = function() { console.log('thing'); }
sinon.spy(el, 'thing');
el.thing();
console.log(el.thing.called);
});
// prints:
// thing
// true
It also seems like that from the docs:
sinon.spy(object, "method") creates a spy that wraps the existing function object.method. The spy will behave exactly like the original method (including when used as a constructor), but you will have access to data about all calls.
I am using Jasmine to test an Angular app and would like to test that the getItem() function within my controller is called when the ready() function of the controller is called.
--- Controller ---
var vm = this;
vm.items = [];
$ionicPlatform.ready(ready);
function ready() {
vm.items.push(getItem());
function getItem(){
var item = //do stuff to get item;
console.log('getItem called');
return item;
}
}
--- Spec ---
describe('Controller', function(){
//--- Load app with dependencies and module to test code omitted.
beforeEach(function(){
//How do I spy on getItem() to test that it was called?
//I've tried getItem = jasmine.createSpy()
//I've tried spyOn(window, 'getItem')
}
//--- Initialize the controller and a mock scope code omitted.
beforeEach(function(done){
$ionicPlatform.ready(function(){
done();
});
});
it('getItem function should get called', function(){
expect(getItem).toHaveBeenCalled();
//--- Note, getItem does not get called according to the expect statement,
//--- but does output 'getItem called' to the terminal when running the test.
});
});
Unfortunately, you've come upon a fundamental limit of Javascript unit testing with Jasmine-- you can only spyOn methods that are exposed on some object. If there is a function that is internal to another function, and not exposed in anyway, you cannot test it directly.
However, you do have two options available to you:
Expose the function in a way that it can be spied on (generally as a method of whatever Angular component you are testing).
Test it indirectly.
The first is probably relatively self-evident, but the latter may be a little confusing. Basically, you can't test directly if the getItems function is called, but the function may have downstream methods it calls or values it changes you can test. For instance, you can test that vm.items.push is larger after ready is called, or you can spyOn(console.log) and expect(console.log).toHaveBeenCalledWith('getItem called').
You can find arguments for both approaches on the internet-- I tend to prefer approach two because I don't like doing refactors solely for the purpose of testability, but many will argue that refactoring for testability generally yields better code. That choice is yours to make. Hope this helps!
I have a method
thisSvc.asyncOperation: function(id) {
return thatSvc.getById(id);
Is it possible to create a spy that will tell me if thatSvc.getById has been called, or is this design an anti-pattern? AFAIK, spies can only be created on a single object.
You can spy on whatever you want, in your jasmine tests just make sure you get that service:
var thisSvc, thatSvc;
beforeEach(inject(function(_thisSvc_, _thatSvc_){
thisSvc = _thisSvc_;
thatSvc = _thatSvc_;
});
it('.asyncOperation should call thatSvc.getById', function(){
spyOn(thatSvc, 'getById');
var id = 4;
thisSvc.asyncOperation(id);
expect(thatSvc.getById).toHaveBeenCalledWith(id);
})
From http://www.htmlgoodies.com/html5/javascript/spy-on-javascript-methods-using-the-jasmine-testing-framework.html#fbid=ib4OX6qA3oS
"spyOn() can only be used when the method already exists on the object. For simple tests, this is your best bet."
How do I use sinon.js to mock/spy object mentioned inside the javascript function?
The mentioned object makes a method call as well and I need to test if the method is called by that object.
Any help will be appreciated.
Thanks a Million in advance!
Awaiting any response.
var ABCClient = require('ABCClient');
var connect = function(){
var client;
client = new ABCClient(); //instantiating object
client.on('some parameter'); // Test if the `on` event is called.
}
Looking at your mock. This is fairly straight forward once you step back and think about it. You are creating an instance of a class. Simply do this:
var ABCClient = require('ABCClient');
describe('test', function() {
it('some test', function() {
var stub = sinon.stub(ABCClient.prototype, 'on').yields('return object');
assert.ok(stub.calledWith('Parameter'));
ABCClient.prototype.on.restore();
});
});
Alternatively, you can just use spy over stub if you just want to spy on it without changing behaviour.
In this sample, you are stubbing the prototype such that all the instances will have that property/method stubbed. Thus, you will be able to access the object. Give it a try and let me know.
I looked at the other questions regarding spying on functions in Jasmine but I didn't get my doubt answered there. I intend to use andCallThrough to track my original function in my src script. This is what I have:
describe("My Test to spy :", function() {
var mySpy = jasmine.createSpy(window, "login");
beforeEach(function(){
mySpy();
});
it("Expects login() will be called", function(){
expect(mySpy).toHaveBeenCalled();
});
});
So this test passes because its the spy that is being called right? Not the original implementation of the function. So if I use mySpy.andCallThrough() it gives an error. The docs are all about chaining objects and for methods. Nothing for functions. Need some help.
The problem is that you use createSpy instead of spyOn. createSpy will create a new spy so you can't use andCallThrough on it as there is no function to call. Using spyOn will replace an existing function with the spy and save the old function in the spy. So when you use andCallThrough it will call this old method.
You can use createSpy but then you have to pass a name and the original function:
jasmine.createSpy('someName', window.login)
When you use `spyOn', you have to pass the an object holding the function and the name of the function:
jasmine.spyOn(window, 'login')