Jasmine conditional callThrough and callFake - javascript

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);
}
});

Related

How to mock a function in Karma.js

Does somebody knows how to mock a function result with karma.js ?
The function uses a var given by thymeleaf framework (java, spring boot, etc..).
function isFlooring() {
var isMyChoiceOk = [[${mychoice}]];
if(typeof isMyChoiceOk !== 'undefined') {
return isMyChoiceOk;
}
else {
return false;
}
}
What I want to do is to tell karma.js that the result of this function is TRUE or FALSE.
You can create a spy on the function (this assumes your function isn't part of an object) and then replace it with your own function:
spyOn(window, 'isFlooring')
.and.callFake( function(arguments) {
// return whatever you want to here
return true
}
The spy just listens for that function to be called and then 'callFake' replaces the functionality with what you want it to be.
If your function is part of an object, replace 'window' in the 'spyOn' call with the name of the object.

How to spyon jquery selector [duplicate]

When it comes to spying on jQuery functions (e.g. bind, click, etc) it is easy:
spyOn($.fn, "bind");
The problem is when you want to spy on $('...') and return defined array of elements.
Things tried after reading other related answers on SO:
spyOn($.fn, "init").andReturn(elements); // works, but breaks stuff that uses jQuery selectors in afterEach(), etc
spyOn($.fn, "merge").andReturn(elements); // merge function doesn't seem to exist in jQuery 1.9.1
spyOn($.fn, "val").andReturn(elements); // function never gets called
So how do I do this? Or if the only way is to spy on init function how do I "remove" spy from function when I'm done so afterEach() routing doesn't break.
jQuery version is 1.9.1.
WORKAROUND:
The only way I could make it work so far (ugly):
realDollar = $;
try {
$ = jasmine.createSpy("dollar").andReturn(elements);
// test code and asserts go here
} finally {
$ = realDollar;
}
Normally, a spy exists for the lifetime of the spec. However, there's nothing special about destroying a spy. You just restore the original function reference and that's that.
Here's a handy little helper function (with a test case) that will clean up your workaround and make it more usable. Call the unspy method in your afterEach to restore the original reference.
function spyOn(obj, methodName) {
var original = obj[methodName];
var spy = jasmine.getEnv().spyOn(obj, methodName);
spy.unspy = function () {
if (original) {
obj[methodName] = original;
original = null;
}
};
return spy;
}
describe("unspy", function () {
it("removes the spy", function () {
var mockDiv = document.createElement("div");
var mockResult = $(mockDiv);
spyOn(window, "$").and.returnValue(mockResult);
expect($(document.body).get(0)).toBe(mockDiv);
$.unspy();
expect(jasmine.isSpy($)).toEqual(false);
expect($(document.body).get(0)).toBe(document.body);
});
});
As an alternative to the above (and for anyone else reading this), you could change the way you're approaching the problem. Instead of spying on the $ function, try extracting the original call to $ to its own method and spying on that instead.
// Original
myObj.doStuff = function () {
$("#someElement").css("color", "red");
};
// Becomes...
myObj.doStuff = function () {
this.getElements().css("color", "red");
};
myObj.getElements = function () {
return $("#someElement");
};
// Test case
it("does stuff", function () {
spyOn(myObj, "getElements").and.returnValue($(/* mock elements */));
// ...
});
By spying on the window itself you have access to any window properties.
As Jquery is one of these you can easily mock it as below and return the value you require.
spyOn(window, '$').and.returnValue(mockElement);
Or add a callFake with the input if it needs to be dynamic.

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.

Angular services with default values for non-existing attributes

Working on an Ionic application that performs both in Android and Windows.
There are services, such as Ionic's $ionicLoading, which we override functionality in order to work properly in windows:
angular.factory('$ionicLoading', function(){
return {
show: function (){...} // custom implementation
hide: function (){...} // custom implementation
}
});
But there are other services which we have to override only to not break the app.
In this cases it would be really useful to provide a service that won't do anything. For example:
angular.factory('$ionicExampleService', function(){
return {
*foo*: angular.noop // for operations
*bar*: promise // returns promise
}
});
Note: I know that a better way of doing this would be with a service that chooses between Ionic's implementation or a made one, but this is just for the sake of learning.
The ideal would be going even further, it would be magnificent to be able to return something even more bulletproof. Something like a generic flexible services:
angular.factory('$ionicPopup', function(){
return /*magic*/;
});
$ionicPopup.show({...}) // show was not defined
.then(foo); // won't break and will execute foo()
It is possible?
From what I understood you need to override implementation of existing services. You can do that with an angular service decorator.
A service decorator intercepts the creation of a service, allowing it to override or modify the behaviour of the service. The object returned by the decorator may be the original service, or a new service object which replaces or wraps and delegates to the original service.
For more information you can check angular documentation. One simple example would be:
app.factory('someService', function () {
return {
method1: function () { return '1'; }
method2: function () { return '2'; }
};
});
app.decorator('someService', function ($delegate) {
// NOTE: $delegate is the original service
// override method2
$delegate.method2 = function () { return '^2'; };
// add new method
$delegate.method3 = function () { return '3'; };
return $delegate;
});
// usage
app.controller('SomeController', function(someService) {
console.log(someService.method1());
console.log(someService.method2());
console.log(someService.method3());
});
EDIT: Question - How to override every method in the service?
var dummyMethod = angular.noop;
for(var prop in $delegate) {
if (angular.isFunction($delegate[prop])) {
$delegate[prop] = dummyMethod;
}
}
I hope that this helps you.
Using an evaluation for each assignment based on an object property, similar to this:
myVar = myObj.myPropVar === undefined ? "default replacement" : myObj.myPropVar;
Basically you're using a check for if the property has been defined, substituting a default value if it hasn't, and assigning it if it has.
Alternatively, you can use a modified version of the global function in Sunny's linkback to define defaults for all those properties you might assume to be undefined at specific points in your code.
function getProperty(o, prop) {
if (o[prop] !== undefined) return o[prop];
else if(prop == "foo") return "default value for foo";
else if(prop == "bar") return "default value for bar";
/* etc */
else return "default for missing prop";
}
Hope that helps,
C§
use var a = {}; to declare new variable.

Evaluate all functions in Javascript Object

I've created a Javascript object with a layout like this:
var myObject : {
doSomething : function (args) {
//do some stuff
return result;
}
//note the args for both functions are of the same format
doSomethingElse : function (args){
//do some other stuff
return otherResult
}
}
Now I want to achieve the following, but am not sure on how to do it in a clean way:
console.log(myObject(args));
/*
Output:
{
doSomething : result,
doSomethingElse : otherResult
}
*/
I'd like to keep both functions separate, as I would like to be able to refer to them in separate instances, but also evaluate both at once to get the desired output as above.
Thanks in advance for your help!
I'd imagine you'd have to iterate, something like:
var results = {},
args = "some args";
for (var key in myObject) {
if (typeof myObject[key] === "function") results[key] = myObject[key](args);
}
console.log(results); //should be the output you want (untested)
Add one more method that calls both methods (passing them the arguments) and returns your desired object output. Something like this would do:
...
doBoth: function (args) {
return {
doSomething: this.doSomething(args),
doSomethingElse: this.doSomethingElse(args)
};
}
...
Calling myObject.doBoth(args) will return the result you hoped for.
jsFiddle Demo
You can also do something a bit more advanced, for example listing the method names you expect to be run for the result:
...
doThese: function (methods, args) {
var result = {};
methods.forEach(function (m) {
result[m] = this[m](args);
}, this);
return result;
}
...
You could invoke this with myObject.doThese(['doSomething', 'doSomethingElse'], args).
jsFiddle Demo
I would advise you not to run all the methods on the object (like other answers suggest). Seems easier at first, but will make your object difficult to modify and cause unintended behaviour in the future.

Categories

Resources