Jasmine .calls.count and .calls.any throwing error - javascript

I am trying to test .calls.count() and .calls.any, I tried the below code from this link Jasmine test cases
describe("A spy", function() {
var foo, bar = null;
beforeEach(function() {
foo = {
setBar: function(value) {
bar = value;
}
};
spyOn(foo, 'setBar');
});
it("tracks if it was called at all", function() {
expect(foo.setBar.calls.any()).toEqual(false);
foo.setBar();
expect(foo.setBar.calls.any()).toEqual(true);
});
it("tracks the number of times it was called", function() {
expect(foo.setBar.calls.count()).toEqual(0);
foo.setBar();
foo.setBar();
expect(foo.setBar.calls.count()).toEqual(2);
});
});
But this throws error saying:
TypeError: 'foo.setBar.calls.any' is not a function
and
TypeError: 'foo.setBar.calls.count' is not a function
I checked the syntax, it's same everywhere on the net. What's wrong?

Kind of a silly question, but are you sure you're not accidentally using Jasmine 1.3? calls was a valid property there too, but it didn't yet have the any() and count() methods, which you're referencing in the 2.0 docs.
(I've faced a lot of similar mistakes while migrating from 1.3 to 2.0, because the syntax is very similar, so the mistakes don't stand out at a glance.)

I had a similar problem, and then looked up this. The answer above is right, and I managed to fix it by explicitly including a reference to "jasmine2" in my testem.json, instead of just "jasmine":
{
"framework": "jasmine2",
"src_files": [
"filePath1.js",
"filePath2.js",
"filePathEtc.js"
]
}

Related

wrapMethod Meteor methods

I was looking into this presentation, building large meteor applications, and I like the idea of the wrapMethod(), but it seems like I can't use it like on the example.
Here is my code.
Meteor.methods({
'EX.Accounts.Methods.updateProfileData' : function(userId, firstName, secondName) {
check([firstName, secondName], [String]);
Meteor.users.update(userId, {
$set: {
'profile.firstName': firstName,
'profile.lastName': secondName,
'profile.isRegisted': true
}
});
}
});
EX.Accounts.Methods.updateUserProfile = EX.wrapMethod('EX.Accounts.Methods.updateProfileData');
But I got this error.
TypeError: Object # has no method 'wrapMethod'
I'm missing something I know but just can't find any information about this "wrapMethod"
Update
Also try with
_.extend(EX.Accounts.Methods,{
updateUserProfile : EX.Accounts.Methods.updateProfileData
});
Which doesn't return an error but I don't see the method on the global namespace.
EX.Accounts.Methods is clear with no methods.
I think the developer created the method wrapMethod on his PB obejct. As you can see here there is nothing called wrapMethod in Meteor. I guess they wrote something like this:
PB.wrapMethod = function wrapMethod (meteorMethod) {
return function wrappedMeteorMethod (/*arugments*/) {
Meteor.apply(meteorMethod, arguments)
}
}
I think it's kinda neat.
Btw.: As you can see i like to name my anonymous functions. Makes debugging nicer.
In ES6 this becomes a beauty:
wrapMethod(method) {
return (...args) => Meteor.call(method, ...args);
}

How to test that one function is called before another

I have some tightly coupled legacy code that I want to cover with tests. Sometimes it's important to ensure that one mocked out method is called before another. A simplified example:
function PageManager(page) {
this.page = page;
}
PageManager.prototype.openSettings = function(){
this.page.open();
this.page.setTitle("Settings");
};
In the test I can check that both open() and setTitle() are called:
describe("PageManager.openSettings()", function() {
beforeEach(function() {
this.page = jasmine.createSpyObj("MockPage", ["open", "setTitle"]);
this.manager = new PageManager(this.page);
this.manager.openSettings();
});
it("opens page", function() {
expect(this.page.open).toHaveBeenCalledWith();
});
it("sets page title to 'Settings'", function() {
expect(this.page.setTitle).toHaveBeenCalledWith("Settings");
});
});
But setTitle() will only work after first calling open(). I'd like to check that first page.open() is called, followed by setTitle(). I'd like to write something like this:
it("opens page before setting title", function() {
expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle);
});
But Jasmine doesn't seem to have such functionality built in.
I can hack up something like this:
beforeEach(function() {
this.page = jasmine.createSpyObj("MockPage", ["open", "setTitle"]);
this.manager = new PageManager(this.page);
// track the order of methods called
this.calls = [];
this.page.open.and.callFake(function() {
this.calls.push("open");
}.bind(this));
this.page.setTitle.and.callFake(function() {
this.calls.push("setTitle");
}.bind(this));
this.manager.openSettings();
});
it("opens page before setting title", function() {
expect(this.calls).toEqual(["open", "setTitle"]);
});
This works, but I'm wondering whether there is some simpler way to achieve this. Or some nice way to generalize this so I wouldn't need to duplicate this code in other tests.
PS. Of course the right way is to refactor the code to eliminate this kind of temporal coupling. It might not always be possible though, e.g. when interfacing with third party libraries. Anyway... I'd like to first cover the existing code with tests, modifying it as little as possible, before delving into further refactorings.
I'd like to write something like this:
it("opens page before setting title", function() {
expect(this.page.open).toHaveBeenCalledBefore(this.page.setTitle);
});
But Jasmine doesn't seem to have such functionality built in.
Looks like the Jasmine folks saw this post, because this functionality exists. I'm not sure how long it's been around -- all of their API docs back to 2.6 mention it, though none of their archived older style docs mention it.
toHaveBeenCalledBefore(expected)
expect the actual value (a Spy) to have been called before another Spy.
Parameters:
Name Type Description
expected Spy Spy that should have been called after the actual Spy.
A failure for your example looks like Expected spy open to have been called before spy setTitle.
Try this:
it("setTitle is invoked after open", function() {
var orderCop = jasmine.createSpy('orderCop');
this.page.open = jasmine.createSpy('openSpy').and.callFake(function() {
orderCop('fisrtInvoke');
});
this.page.setTitle = jasmine.createSpy('setTitleSpy').and.callFake(function() {
orderCop('secondInvoke');
});
this.manager.openSettings();
expect(orderCop.calls.count()).toBe(2);
expect(orderCop.calls.first().args[0]).toBe('firstInvoke');
expect(orderCop.calls.mostRecent().args[0]).toBe('secondInvoke');
}
EDIT: I just realized my original answer is effectively the same as the hack you mentioned in the question but with more overhead in setting up a spy. It's probably simpler doing it with your "hack" way:
it("setTitle is invoked after open", function() {
var orderCop = []
this.page.open = jasmine.createSpy('openSpy').and.callFake(function() {
orderCop.push('fisrtInvoke');
});
this.page.setTitle = jasmine.createSpy('setTitleSpy').and.callFake(function() {
orderCop.push('secondInvoke');
});
this.manager.openSettings();
expect(orderCop.length).toBe(2);
expect(orderCop[0]).toBe('firstInvoke');
expect(orderCop[1]).toBe('secondInvoke');
}
Create a fake function for the second call that expects the first call to have been made
it("opens page before setting title", function() {
// When page.setTitle is called, ensure that page.open has already been called
this.page.setTitle.and.callFake(function() {
expect(this.page.open).toHaveBeenCalled();
})
this.manager.openSettings();
});
Inspect the specific calls by using the .calls.first() and .calls.mostRecent() methods on the spy.
Basically did the same thing. I felt confident doing this because I mocked out the function behaviors with fully synchronous implementations.
it 'should invoke an options pre-mixing hook before a mixin pre-mixing hook', ->
call_sequence = []
mix_opts = {premixing_hook: -> call_sequence.push 1}
#mixin.premixing_hook = -> call_sequence.push 2
spyOn(mix_opts, 'premixing_hook').and.callThrough()
spyOn(#mixin, 'premixing_hook').and.callThrough()
class Example
Example.mixinto_proto #mixin, mix_opts, ['arg1', 'arg2']
expect(mix_opts.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
expect(#mixin.premixing_hook).toHaveBeenCalledWith(['arg1', 'arg2'])
expect(call_sequence).toEqual [1, 2]
Lately I've developed a replacement for Jasmine spies, called strict-spies, which solves this problem among many others:
describe("PageManager.openSettings()", function() {
beforeEach(function() {
this.spies = new StrictSpies();
this.page = this.spies.createObj("MockPage", ["open", "setTitle"]);
this.manager = new PageManager(this.page);
this.manager.openSettings();
});
it("opens page and sets title to 'Settings'", function() {
expect(this.spies).toHaveCalls([
["open"],
["setTitle", "Settings"],
]);
});
});

Function expected error in an addEventListener block of code

I am developing a Windows Phone app and using HTML/JS to build it. So there is a WinJS reference in my code.
The following is my code:
WinJS.UI.Pages.define("...", {
ready: function (element, options) {
var item = element.querySelector("#item").winControl;
item.addEventListener("iteminvoked", function (eventInfo) {
eventInfo.detail.itemPromise.done(function itemInvoked(item) {
WinJS.Navigation.navigate("...", { newsItem: item.data });
});
}, false);
}
}
When I ran the code above, it threw the error, saying: 0x800a138a - JavaScript runtime error: Function expected and it lead to base.js, which is part of the WinJS language (correct me if I'm wrong). I used breakpoints and "step over" line-by-line and the line before which the error was thrown is: item.addEventListener....
I tried using http://jshint.com and it didn't show any errors, apart from unused variables and undefined variables (WinJS).
Please let me know if I need to show more code.
When I searched for this problem, I learned that it might be because I added a paranthesis somewhere where I wasn't supposed to, hence it's treating it as a function.
your missing a curly bracket
WinJS.UI.Pages.define("...", {
ready: function (element, options) {
var item = element.querySelector("#item").winControl;
item.addEventListener("iteminvoked", function (eventInfo) {
eventInfo.detail.itemPromise.done(function itemInvoked(item) {
WinJS.Navigation.navigate("...", { newsItem: item.data });
});
}, false);
} // HERE
}

Jasmine object “has no method 'andReturn'”

Beginner with Jasmine, very first attempt with Jasmine Spies. I thought I was mimicking the format displayed here (search: "andReturn"), but I'm getting an error that I can't work out:
TypeError: Object function () {
callTracker.track({
object: this,
args: Array.prototype.slice.apply(arguments)
});
return spyStrategy.exec.apply(this, arguments);
} has no method 'andReturn'
No clue what I'm doing wrong. Here's my Spec:
describe('Die', function() {
it('returns a value when you roll it', function() {
var die = Object.create(Die);
spyOn(Math, 'random').andReturn(1);
expect(die.roll()).toEqual(6);
});
});
And the corresponding JS:
var Die =
{
roll: function() {
return Math.floor(Math.random() * 5 + 1);
}
}
Thanks for the help!!!
jasmine 2.0 changed some of the spy syntax. jasmine 2.0 docs
spyOn(Math, 'random').and.returnValue(1);
try this
spyOn(Math, 'random').and.returnValue(1);
I made a jasmine test where I show this kind of mock. andReturn seems to be working. http://jsfiddle.net/LNWXn/
it("has a value of 1 with and return", function() {
spyOn(Math, 'random').andReturn(1);
expect(Math.random()).toBe(1);
});
You have to keep in mind that it's only mocked for the scope of the test.
Here's one with your example that seems to pass. http://jsfiddle.net/LNWXn/2/
Hope this helped!
use and.returnValue() insted of andReturn()

YUI.Test assertion doesn't fail when it is in event's callback

I'm using version 3.4.0
I have a strange problem with YUI.Test assertion. Here is the example:
YUI().use('test', 'node', 'node-event-simulate',
function(Y) {
var runner = Y.Test.Runner;
var someTestCase = new Y.Test.Case({
name : 'SomeMeaningfulName',
setUp : function() {
var test = this;
// create show details link
test.Y$aLink = Y.Node
.create('Show details');
Y.one('body')
.append(test.Y$aLink);
},
tearDown : function() {
this.Y$aLink.remove();
},
testEventListener : function() {
var test = this;
test.Y$aLink
.on('click', function(e) {
e.preventDefault();
// this codes works
console.log('on click event');
// this one doesn't fail
// it "works" perfectly when it's outside of the callback
Y.assert(false, 'false is true');
});
test.Y$aLink.simulate('click');
}
});
runner.add(someTestCase);
runner.run();
});
The assertion never fails when it's inside of the event's callback :(
Documentation doesn't mention this situation...
Maybe I'm doing something wrong, but there is a lack of documentation makes it hard to be sure...
UPDATE
example without wait/resume:
http://jsfiddle.net/op1ekun/Fgra6/2/
and the one with wait/resume
http://jsfiddle.net/op1ekun/Fgra6/5/
UPDATE2
there is a similar case already reported, it touches asynchronous testing issue, but it's not my problem exactly:
http://yuilibrary.com/projects/yuitest/ticket/74
UPDATE3
this seems to be exactly what I'm experiencing and looks a solution already proposed by Billy, the interesting thing is to use dependency injection, it might be worth a try:
http://csausdev.wordpress.com/2011/02/12/unit-testing-callbacks-with-yui-test/
Please help!
Thanks!
YUI Test has a wait and resume mechanism you can leverage in this case. You tell it to wait until a resume method is called, and the resume method takes a callback where you can safely make assertions. In your case it would look like this:
'test event listener': function () {
var test = this;
test.Y$aLink.on('click', function (e) {
e.preventDefault();
test.resume(function () {
Assert.fail('ouch!');
});
});
test.wait();
test.Y$aLink.simulate('click');
}

Categories

Resources