jasmine: scope of this - javascript

From the jasmine documentation (http://jasmine.github.io/2.0/introduction.html):
The this keyword
Another way to share variables between a beforeEach, it, and afterEach is through the this keyword. Each spec’s beforeEach/it/afterEach has the this as the > same empty object that is set back to empty for the next spec’s beforeEach/it/afterEach.
My understanding of this in Javascript is, that this is bounded to the scope of the actual function. So I would expect that it would be bound to the context different contexts (the ones depending on the function) inside beforeEach/it/afterEach.
e.g.
describe('Spec', function (){
var eachThis = null;
beforeEach(function(){
eachThis = this;
});
it('check this', function(){
except(this).toEqual(eachThis);
}
};
So this test should pass.
Did jasmine change the behavior of this or did i get something wrong?

I think your example maybe has some issues, but you are correct in thinking that jasmine manipulates the this reference when using beforeEach, beforeAll, etc.
Here is an illustrative example -- note that all of the expects listed below will pass:
(function() {
describe("without beforeEach", function () {
(function() {
// this is not inside of a beforeEach call
this.dog = "Spot";
alert(this.dog);
})();
it("should not have access to a dog property from `this`", function () {
expect(this.dog).toBeUndefined(); // because there is no `dog` member of the object currently referenced by `this`
});
});
describe("a beforeEach test", function () {
beforeEach(function () {
this.dog = "Spot";
});
it("should work now because we used `beforeEach`", function () {
expect(this.dog).toEqual("Spot");
});
});
})();
In general, your thinking about 'this' being defined within the scope of function is correct, but this implementation in jasmine demonstrates how you can provide a specific object to be referenced by the 'this' keyword if you want to. The typical way this is done in javascript is using Function.prototype.apply() which allows you to pass in an arbitrary object for the this reference as the first parameter of the function.

So this test should pass.
Yes, and it does.
Did jasmine change the behavior of this
It doesn't look like it.
did i get something wrong
You have syntax errors and typos in your code (e.g. except -> expect).
Also, toEqual doesn't test the identity of an object, so even expect(this).toEqual({}); would pass. toBe is a better method here.

Related

Understanding Sinon Spies: What happens when a spy-wrapped method is called?

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.

Angular Unit Test, SpyOn stand-alone function

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!

Factory method reference confusion, and guideline to code it

I have following angular service.
(function () {
"use strict";
angular.module("theModule")
.factory("theService", theServiceFactory);
function theServiceFactory() {
return {
theFn: theFn,
theFailureFn: theFailureFn,
theSuccessFn: theSuccessFn
};
function theFn() {
angular.noop();
}
function theFailureFn() {
theFn();
}
function theSuccessFn() {
this.theFn();
}
}
}());
Functions are defined separately and their references are assigned to object being returned by the factory.
And I have following jasmine test cases.
describe("The Service Test Specifications", function () {
var theService;
beforeEach(module('theModule'));
beforeEach(inject(function(_theService_) {
theService = _theService_;
}));
it("should call 'theFn' from 'theFailureFn'", function () {
spyOn(theService, "theFn");
theService.theFailureFn();
expect(theService.theFn).toHaveBeenCalled();
});
it("should call 'theFn' from 'theSuccessFn'", function () {
spyOn(theService, "theFn");
theService.theSuccessFn();
expect(theService.theFn).toHaveBeenCalled();
});
});
Test Case should call 'theFn' from 'theFailure' is being failed whereas should call 'theFn' from 'theSuccess' is being passed.
From source code it seems object's theFn is referring to function theFn but actually it's not. It is causing first test case to fail. (Q1) At what stage different reference is assigned to object's theFn?
Second test case is passing as theFn is invoked with this inside theSuccess. But using this in that situation is strict mode violation. I like and follow John Papa's style guide so I created factory and defined all functions below it. (Q2) What would be the better way to write such function?
(Q3) If theFailure is written correctly, is there any way to detect in test case that function theFn is called?
Plunkr: http://plnkr.co/edit/PsQTZQlIgWI3OTMaYi7n
It is actually calling theFn() from theFailureFn(), and NOT calling theFn() from the sucesssFn(). It's the opposite result of your test.
As you see from this,
http://plnkr.co/edit/Tp5FtsL8DAjkcPO0m0ZV?p=preview
console.log('theService.theFn BEFORE spyOn', theService.theFn);
spyOn(theService, "theFn");
console.log('theService.theFn AFTER spyOn', theService.theFn);
Result
theService.theFn BEFORE spyOn theFn()
theService.theFn AFTER spyOn j$.createSpy.spy()
your Jasmine spyOn(theService, "theFn"); is setting an instance of theService, this.theFn, and your test is checking this.theFn is called or not. Remember that theService is a Hash, not a function.
As you see from the output of this line;
console.log('caller theFailureFn', this, theFn, this.theFn)
//OUTPUT
caller theFailureFn Object {} theFn() j$.createSpy.spy()
theFn and this.theFn is very different. theFn is an object property value and this.theFn is an instance of object, Hash.
To answer your question,
(Q1) At what stage different reference is assigned to object's theFn
theFn is assigned as you expected. spyOn makkes difference in your case.
(Q2) What would be the better way to write such function?
To test a function calls a function of an object, it should return a function, not a hash.
(Q3) If theFailure is written correctly, is there any way to detect in test case that function theFn is called?
The same answer as Q2.
Dont use this in .factory(). Factory is just executed as function and it just returns what you explicitly specify as return object. Angular uses .service() as a constructor function which is executed with new operator and thats the place where you will use this.
That means that your theFailureFn() is written correctly.
As for Q3, it may fail just because how spyOn is implemented.
Edit:
As i supposed, its the implementation of spyOn().
spyOn wraps the function but in your factory you are still referencing the original function. Try to use expect(this.theFn).toHaveBeenCalled();

Jasmine Spies.and.stub method

I've been reading through the Jasmine documentation and I've been struggling to understand what the Spies .and.stub method actually does. English is not my native language, so I don't even know what the word "stub" actually means, and there is no translation for it in my language.
In the documentation it says:
When a calling strategy is used for a spy, the original stubbing behavior can be returned at any time with and.stub.
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar').and.callThrough();
});
it("can call through and then stub in the same spec", function() {
foo.setBar(123);
expect(bar).toEqual(123);
foo.setBar.and.stub();
bar = null;
foo.setBar(123);
expect(bar).toBe(null);
});
});
What does and.stub actually do and how is it useful?
For the term, you can look at wikipedia : http://en.wikipedia.org/wiki/Test_stub
In a nutshell it's a "fake" object that you can control that replaces a "real" object in your code.
For the function, what I understand is that and.stub() removes the effect of and.callThrough() on a spy.
When you call and.callThrough, the spy acts as a proxy, calling the real function, but passing through a spy object allowing you to add tests like expectation.
When you call and.stub, or if you never call and.callThrough, the spy won't call the real function. It's really usefull when you don't want to test an object's behavior, but be sure that it was called. Helping you to keep your test truly unitary.
To complete the previous answer:
Indeed, it's not clear from the doc, but it's very clear in the source code:
https://github.com/jasmine/jasmine/blob/4be20794827a63ca5295848c58ffc478624ee4dc/src/core/SpyStrategy.js
plan = function() {};
-> the called function is empty
this.callThrough = function() {
plan = originalFn;
-> the called function is the original function
this.stub = function(fn) {
plan = function() {};
-> the called function is empty (again)

Jasmine spyOn: How to make it work when the spyed function reference is passed?

I'm using jasmine to test my javascript, and I'm finding a hard case to spec. I have this function which is being passed directly to the handler:
filters.find('#per_page').change(checkList.filter);
Now, I'd like to spy on it and see if it's being called when i change that:
describe("when I change the number of items per page", function() {
beforeEach(function()
spyOn(checkList, 'filter');
$('#per_page').val('50').trigger('change');
});
it("filters the results list", function() {
expect(checkList.filter).toHaveBeenCalled();
});
});
This test fails. Suprisingly, if I change my code above to:
filters.find('#per_page').change(function() { checkList.filter(); });
it passes. Is there a way to spy on such functions passed or do I have to resort to the anonymous function case?
Yes, there is, but to do so events have to be bound after you have added the spies on your function. The reason for this is that spies replace the property with an other function (it doesn't change the function itself). In your case, what happened is that you assigned the original function and when you added the spies you replaced the property without changing the callback that was bound.
If the way your application is made you can't add your spies before doing the events binding, than the only it can work is to use anonymous function which call the proper callback.

Categories

Resources