Jasmine: Testing toHaveBeenCalledWith(params) where params is an object - javascript

I'm doing some testing with Jasmine, having a strange result when looking for expected params as a result of a Spy.
I'm testing using the toHaveBeenCalledWith() method and looking for something like this:
{
foo: bar,
thing: [1,2,3],
etc: function() { blah; }
}
It gives a fail, but the error message seems to confirm the exact same object is actually being found.
Any reasons why this might be the case?

Equivalent function definitions are not equal. So,
function() { return 'blah'; } == function() { return 'blah'; } // returns false
You have to reference the same function definition when using toHaveBeenCalledWith() for Jasmine to consider it equal to the argument passed to the spied object's method. Are you maybe passing in a new object definition (like the object you included in your question) to toHaveBeenCalledWith()? That will not pass the Jasmine assertion.
Here is the Jasmine spec I ran which illustrates what I said. There is a succeeding example and a failing example:
describe("A spy example", function() {
var baz = {
foo: 'bar',
thing: [1,2,3],
etc: function() { }
};
beforeEach(function() {
spyOn(baz, 'etc');
baz.etc(baz);
});
it("succeeds", function() {
expect(baz.etc).toHaveBeenCalledWith(baz);
});
it("fails", function() {
expect(baz.etc).toHaveBeenCalledWith({
foo: 'bar',
thing: [1,2,3],
etc: function() { }
});
});
});

A possible solution is to use: Custom asymmetric equality tester. Which let's the tester decide how to determine equality.
Example:
describe("A spy example", function() {
var baz = {
foo: 'bar',
thing: [1,2,3],
etc: function() { }
};
beforeEach(function() {
spyOn(baz, 'etc');
baz.etc(baz);
});
it("succeeds", function() {
expect(baz.etc).toHaveBeenCalledWith(baz);
});
var customTester = {
asymmetricMatch: function(actual) {
return actual.foo === 'bar' &&
actual.thing.length === 3 // && ... ( any deep comparison method you wish to use)
}
};
it("succeeds too", function() {
expect(baz.etc).toHaveBeenCalledWith(customTester);
});
});
Hope this helps.

Related

How do I chain Intern Page Object function calls?

Following the Intern user guide, I wrote a simple page object:
define(function(require) {
function ListPage(remote) {
this.remote = remote;
}
ListPage.prototype = {
constructor: ListPage,
doSomething: function(value) {
return this.remote
.get(require.toUrl('http://localhost:5000/index.html'))
.findByCssSelector("[data-tag-test-id='element-of-interest']")
.click().end();
}
};
return ListPage;
});
In the test, I want to call doSomething twice in a row, like this:
define(function(require) {
var registerSuite = require('intern!object');
var ListPage = require('../support/pages/ListPage');
registerSuite(function() {
var listPage;
return {
name: 'test suite name',
setup: function() {
listPage = new ListPage(this.remote);
},
beforeEach: function() {
return listPage
.doSomething('Value 1')
.doSomething('Value 2');
},
'test function': function() {
// ...
}
};
});
});
However, when I run the test, I get this error:
TypeError: listPage.doSomething(...).doSomething is not a function
I tried some approaches described in this question, to no avail.
A better way to implement page objects with Intern is as helper functions rather than Command wrappers. Groups of related helper functions can then be used to create Page Object modules.
// A helper function can take config parameters and returns a function
// that will be used as a Command chain `then` callback.
function doSomething(value) {
return function () {
return this.parent
.findByCssSelector('whatever')
.click()
}
}
// ...
registerSuite(function () {
name: 'test suite',
'test function': function () {
return this.remote.get('page')
// In a Command chain, a call to the helper is the argument
// to a `then`
.then(doSomething('value 1'))
.then(doSomething('value 2'));
}
}

Jasmine custom matcher with Protractor for checking browser title

I am writing a Jasmine custom matcher to use in a Protractor spec and I want to check that the browser title is equal to some string. I am unable to get this code to work properly and after spending hours debugging it, I can only assume that the browser object is not being accessed inside the matcher function as I expect it to be. When I modify the matcher function to accept browse.getTitle() as the actual argument then it works fine, which leads me to my assumption. Can anyone find the issue here and explain it to me?
beforeEach(function() {
jasmine.addMatchers({
toBeOnPage: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var result = {};
result.pass = actual.getTitle() === expected.title;
return result;
}
};
}
});
});
var homepage = { url: 'Homepage URL', title: 'Homepage Title' };
describe('regression:', function() {
it('homepage loads successfully', function() {
browser.get('http://localhost/#/home');
expect(browser).toBeOnPage(homepage);
});
});
The problem is that getTitle() returns a promise. Resolve it:
beforeEach(function() {
jasmine.addMatchers({
toBeOnPage: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
return {
pass: actual.getTitle().then(function (title) {
return title === expected.title;
});
};
}
};
}
});
});

Has been called with object assertion

Function I'm spying on, receives object as an argument. I need to assert that the function been called with certain properties of the object.
e.g: my SUT has:
function kaboom() {
fn({
foo: 'foo',
bar: 'bar',
zap: function() { ... },
dap: true
});
}
and in my test I can do this:
fnStub = sinon.stub();
kaboom();
expect(fnStub).to.have.been.called;
and that works (it's good to know that fn's been called). Now I need to make sure that the right object has been passed into the function. I care about only foo and bar properties, i.e. I have to set match for specific properties of the argument. How?
upd: sinon.match() seems to work for simple objects. Let's raise the bar, shall we?
What if I want to include zap function into assertion? How do I make that work?
Assuming you're using sinon-chai, you can use calledWith along with sinon.match to achieve this
expect(fnStub).to.have.been.calledWith(sinon.match({
foo: 'foo',
bar: 'bar'
}));
To achieve called.with partial object check, you can also use chai-spies-augment package (https://www.npmjs.com/package/chai-spies-augment) :
Usage (please, notice to .default after importing):
const chaiSpies = require('chai-spies');
const chaiSpiesAugment = require('chai-spies-augment').default;
chai.use(chaiSpies);
chai.use(chaiSpiesAugment);
usage :
const myStub = { myFunc: () => true };
const spy1 = chai.spy.on(myStub, 'myFunc');
myStub.myFunc({ a: 1, b: 2 });
expect(spy1).to.have.been.called.with.objectContaining({ a: 1 });

reuqure.js how to separate main module to submodules

guys. I have some question about how can i use require.js in my project.
I have some module. For example:
obj.js
var MyModule = (function(){
var myObject = function(){
this.options = {
foo: 'foo',
bar: 'bar'
};
};
myObject.prototype.foo = function(){
console.log(this.options.foo);
};
myObject.prototype.bar = function(){
console.log(this.options.bar);
};
return {
getMyObject : function(){
return new myObject();
}
}
})();
I haven't problems with using my object as solid. I appended to obj.js this code:
define(function(){
return MyModule;
})
And use it in my app.js module
requirejs.config({
baseUrl : 'lib',
paths : {
app:'../js/app'
}
});
require(['app/obj'], function(MyModule){
var someObj = MyModule.getMyObject();
someObj.foo();
someObj.bar();
});
It's all ok. But how can i split MyModule to some submodules? For example. In module i have 1 object, its constructor and two its methods. Can i put constructor to file, for example 'mymodule.myobject.js', foo to 'mymodule.myobject.foo.js' and bar to 'mymodule.myobject.bar.js' and use it with require.js? If it's really, how can i do this?
You can use this way. Expect following file structure
- js
-- constructors
--- objectCostructor.js
-- objects
--- objectModule.js
So the objectCostructor.js is:
define(function(){
return myObject = function(){
this.options = {
foo: 'foo',
bar: 'bar'
}
}
})
And objectModule.js
define(['constructors/objectCostructor'], function(Const){
Const.prototype.foo = function(){
console.log(this.options.foo);
};
Const.prototype.bar = function(){
console.log(this.options.bar);
};
return {
getMyObject : function(){
return new Const();
}
}
})

RequireJS and Prototypal Inheritance

I'm running into an issue with using RequireJS and Prototypal inheritance. Here's my module:
define(function () {
function Module(data) {
this.data = data;
}
Module.prototype.getData = function () {
return this.data;
};
Module.prototype.doSomething = function () {
console.log(this.data);
console.log(this.getData());
};
return Module;
Module.prototype.callFunction = function (fn) {
if (this[fn]) {
console.log('call');
Module.prototype[fn]();
}
};
});
Then I instantiate the module, like so:
var module = new Module({ name: 'Marty' });
module.getData(); // returns { name: 'Marty' }
module.data; // returns { name: 'Marty' }
module.callFunction('doSomething') // returns undefined on the first (and second) console log
The console.logs in the module.doSomething() always return undefined. Am I misunderstanding how prototypal inheritance works with RequireJS?
As it turns out, I had written the callFunction method incorrectly. The correct way is:
Module.prototype.callFunction = function (fn) {
if (this[fn] && typeof this[fn] === "function") {
this[fn]();
}
};
The problem was using Module.prototype instead of this. Whoops.

Categories

Resources