custom matcher makes jasmine to hang - javascript

Trying to call this custom matcher in jasmine testing tool but I got this error:
TypeError: matcherCompare is undefined
var result = matcherCompare.apply(null, args);
jasmine.js (line 1192)
My matcher:
/*
* Extends jasmine expectations by defining new matchers
*/
beforeEach(function () {
jasmine.addMatchers({
toEqualArray: function(){
var s = typeof this.actual,
result = false;
if (s === 'object'){
if (this.actual){
if (Object.prototype.toString.call(this.actual) === Object.prototype.toString.call([])) { //'[object Array]'
result = true;
}
}
}
this.message = function(){
if (result){
return "Is Array";
}
return "Is not an Array";
};
return result;
}
});
});
The core of the code inside toEqualArray is already tested as a simple js function and is ok. My matcher doesn't have an argument as you can see. I use jasmine 2.0 standalone for my tests and my matcher resides in an external js file like in the example at the standalone version of jasmine. I even moved my matcher inside my specs replacing jasmine with this but with no result!
What am I doing wrong?
Jasmine hangs when I put in my spec this specific command:
expect(o.get('any')).toEqualArray();
where o is my object that returns (I tested and it's ok) an array!
I have to debug jasmine now :(

For jasmine 2.0, the syntax for custom matchers was changed. Updated documentation is here: http://jasmine.github.io/2.0/custom_matcher.html

Related

How to recover Jasmine spy name in custom matcher

Im going to create a custom matcher in Jasmine 2.0 to verify spies against some additional conditions. In huge simplification, something like:
var customMatchers = {
toDoSomething: function(util, customEqualityTesters) {
return {
compare: function(spy) {
var comparison = {};
comparison.pass = testSomeCondition(spy);
if (!comparison.pass) {
comparison.message = "Expect " + /insert code here/ + " to do something";
}
return comparison;
}
}
}
};
beforeEach(function() {
jasmine.addMatchers(customMatchers);
});
My question is, how to recover the spy name, passed as a first argument of factory method: createSpy(name, originalFn)?
I cannot find anything in Jasmine documentation v2.6 neither in online tutorials.
console.log(spy) returns function(...) {...}
I don't know if it is the right thing to do so, but found in Jasmine source code how toHaveBeenCalled was originally implemented and found:
spy.and.identity()
It works well also in a custom matcher.

Jasmine conditional callThrough and callFake

I have a method which returns function references.
function methodetobeMoked(param){
case1:return func1;
case 2: return func2;
.
.
case n: return funcN;
}
I need to spy this method and return a fake function reference for a particular input param p
Is there any conditional callThrough in jasmine tests
My scenario is
SpyOn(some object,'someMethode').and.{if param=p callFake(fakeMethode) else callThrough()}
I tried callFake Is there any way to pass control to original method from fake method?
A Jasmine spy retains the original function in a property named originalValue, so you can do something like:
var mySpy = {};
mySpy = t.spyOn(obj, 'methodToBeMocked').and.callFake(function (param) {
if (param === 'fake case') {
// return fake result
} else {
// do this if using Jasmine
return (mySpy.and.callThrough())(param);
// do this if using Ext + Siesta and duped by common syntax :)
// return mySpy.originalValue(param);
}
});

How can I migrate my custom matchers from Jasmine 1 to Jasmine 2

Version 2 of the JavaScript testing framework jasmine unfortunately introduced several breaking changes. One of these changes is the way custom matchers are handled, as is outlined here:
http://jasmine.github.io/2.0/upgrading.html
The addMatchers function is no longer on the spec (this) it is now on the global jasmine object.
/* was:
this.addMatchers({
*/
jasmine.addMatchers({
A matcher is set up a bit different now. The factory receives a util object which contains things like jasmines equality functions, and any registered customEqualityTesters. The factory is expected to return an object with a compare function which will be called with the actual and expected directly, instead of the actual value being on this
/* was:
toBeCustom: function(expected) {
var passed = this.actual == expected;
*/
toBeCustom: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var passed = actual == expected
The comparison should now return an object with pass and message attributes.
I am looking for an easy way to migrate our existing matchers, so that we can easily switch to the new jasmine version.
To ease the transition to the new jasmine version the following special migration object will help.
Instead of adding the matcher on the this object, you add them on the jasmineMigrate object. But this is really all you need to to. The jasmineMigrate object will take care of the rest.
/* was:
this.addMatchers({
*/
jasmineMigrate .addMatchers({
The implementation of the migration object:
var jasmineMigrate = {};
jasmineMigrate.addMatchers = function (matchers) {
Object.keys(matchers).forEach(function (matcherName) {
var matcher = matchers[matcherName],
migratedMatcher = {};
migratedMatcher[matcherName] = function (util, customEqualityTesters) {
return {
compare: function (actual) {
var matcherArguments,
thisForMigratedMatcher,
matcherResult,
passed;
//In Jasmine 2 the first parameter of the compare function
//is the actual value.
//Whereas with Jasmine 1 the actual value was a property of the matchers this
//Therefore modify the given arguments array and remove actual
matcherArguments = [].slice.call(arguments)
matcherArguments.splice(0, 1);
//Add actual to the this object we'll be passing to the matcher
thisForMigratedMatcher = {
actual: actual
};
//Now call the original matcher aufgerufen, with the modified
//arguments and thisForMigratedMatcher which will be applied to
//the matcher
passed = matcher.apply(thisForMigratedMatcher, matcherArguments);
matcherResult = {
pass: passed,
message: thisForMigratedMatcher.message
};
return matcherResult;
}
}
};
jasmine.addMatchers(migratedMatcher);
});
}
The add-matchers library lets you write matchers which are compatible with Jasmine v1, Jasmine v2, and Jest.

Using external class during client side Mocha unit testing

I am running unit tests on a javascript class using Mocha using the follow methodology, firstly the test:
var base = require('../moduleone.js');
describe("some test", function() {
it("description", function() {
var check = base.toBeTested(dummyValue)
//test is here ...
});
});
the moduleone.js containing function to be tested:
function toBeTested(category){
//below I calling an assert function defined in moduletwo
//works fine when running in browser
assert(type(category)=='string','category is string type');
//more code..
return something
module.exports.toBeTested = toBeTested;
moduletwo.js:
function assert(outcome, description) {
//see code.tutsplus.com quick and easy javascript testing with assert
var li = outcome ? 'pass' : 'fail';
if (li == 'fail') {
console.log('FAIL: '+description);
}
else {
console.log('PASS: '+description);
}
}
The issue I have is mocha doesn't know anything about moduletwo and when the moduleone function calles the function in moduletwo mocha throws a ReferenceError: assert is not defined. How can I link all my dependencies so that mocha can see them?
In your moduleone.js be sure that you are requireing moduletwo.js to bring your assert function into scope for moduleone.js. Otherwise, you get a ReferenceError, not for any reasons with mocha, but because moduleone does not have access to assert.
// moduletwo.js
function assert(outcome, description) { /* your functionality */ }
module.exports = assert
// moduleone.js
var assert = require('./moduletwo')
function toBeTested(category) { /* your functionality that uses assert */ }
module.exports.toBeTested = toBeTested
Now, with regards to that tutorial. If you are following it to learn how to make an easy assert module, that is fine. But if you are trying to test some code that does something else, consider using an existing assertion library like chai. For example:
// example.test.js
var expect = require('chai').expect
var isValidCategory = require('./is-valid-category')
describe('isValidCategory(category)', function () {
it('validates string categories', function () {
expect(isValidCategory('A String Category')).to.be.true
})
it('does not validate non-string categories', function () {
expect(isValidCategory(['an', 'array', 'somehow'])).to.be.false
})
})
// is-valid-category.js
module.exports = function isValidCategory(category) {
return typeof category === 'string'
}

Jasmine tests pass in Chrome and Firefox but fail with PhantomJS

I am building a basic blogging app with React. I am using Jasmine and Karma to run my front end tests. I got my first test up and running and it passes in Chrome (Chromium) and Firefox, but when it runs in PhantomJS I get the following error:
PhantomJS 1.9.8 (Linux 0.0.0) ERROR
TypeError: 'undefined' is not a function (evaluating 'ReactElementValidator.createElement.bind(
null,
type
)')
at /home/michael/repository/short-stories/test/karma_tests/story_test.js:1742
My test file looks like this:
var React = require('react/addons');
var Story = require('../../app/js/components/story.jsx');
var TestUtils = React.addons.TestUtils;
var testUtilsAdditions = require('react-testutils-additions');
describe('Story component', function () {
var component;
beforeEach(function () {
component = TestUtils.renderIntoDocument(React.createElement('story'));
component.props.storyTitle = 'front end test title';
component.props.author = 'front end author';
component.props.storyText = 'front end story text';
});
it('should display a story', function () {
expect(component.props).toBeDefined();
expect(component.props.storyTitle).toBeDefined();
expect(component.props.storyTitle).toBe('front end test title');
expect(component.props.author).toBe('front end author');
expect(component.props.storyText).toBe('front end story text')
});
});
I tried deleting my node_modules, and the npm cache clear and npm install, but it didn't fix it. I'm not sure how my tests could pass in Firefox and Chrome, but not in PhantomJS. You can see the full project here: https://github.com/mrbgit/short-stories . Let me know if there's any more info that could help. Any help is appreciated. Thanks!
PhantomJS uses a rather old version of Qt-Webkit that does not provide Function.prototype.bind. This is a problem for a lot of libraries, so a polyfill NPM module called 'phantomjs-polyfill' is available.
If you'd rather not use NPM modules (if you're testing a browser site that hasn't been bundled with browserify/webpack), the following polyfill for bind is provided on the MDN page and you can attach it yourself:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}

Categories

Resources