How to spyOn a value property (rather than a method) with Jasmine - javascript

Jasmine's spyOn is good to change a method's behavior, but is there any way to change a value property (rather than a method) for an object? the code could be like below:
spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);

In February 2017, they merged a PR adding this feature, they released in April 2017.
so to spy on getters/setters you use:
const spy = spyOnProperty(myObj, 'myGetterName', 'get');
where myObj is your instance, 'myGetterName' is the name of that one defined in your class as get myGetterName() {} and the third param is the type get or set.
You can use the same assertions that you already use with the spies created with spyOn.
So you can for example:
const spy = spyOnProperty(myObj, 'myGetterName', 'get'); // to stub and return nothing. Just spy and stub.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.returnValue(1); // to stub and return 1 or any value as needed.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.callThrough(); // Call the real thing.
Here's the line in the github source code where this method is available if you are interested.
https://github.com/jasmine/jasmine/blob/7f8f2b5e7a7af70d7f6b629331eb6fe0a7cb9279/src/core/requireInterface.js#L199
And the spyOnProperty method is here
Answering the original question, with jasmine 2.6.1, you would:
const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();

Any reason you cannot just change it on the object directly? It is not as if javascript enforces visibility of a property on an object.

The best way is to use spyOnProperty. It expects 3 parameters and you need to pass get or set as a third param.
Example
const div = fixture.debugElement.query(By.css('.ellipsis-overflow'));
// now mock properties
spyOnProperty(div.nativeElement, 'clientWidth', 'get').and.returnValue(1400);
spyOnProperty(div.nativeElement, 'scrollWidth', 'get').and.returnValue(2400);
Here I am setting the get of clientWidth of div.nativeElement object.

Jasmine doesn't have that functionality, but you might be able to hack something together using Object.defineProperty.
You could refactor your code to use a getter function, then spy on the getter.
spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);

The right way to do this is with the spy on property, it will allow you to simulate a property on an object with an specific value.
const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();

If you are using ES6 (Babel) or TypeScript you can stub out the property using get and set accessors
export class SomeClassStub {
getValueA = jasmine.createSpy('getValueA');
setValueA = jasmine.createSpy('setValueA');
get valueA() { return this.getValueA(); }
set valueA(value) { this.setValueA(value); }
}
Then in your test you can check that the property is set with:
stub.valueA = 'foo';
expect(stub.setValueA).toHaveBeenCalledWith('foo');

Suppose there is a method like this that needs testing
The src property of the tiny image needs checking
function reportABCEvent(cat, type, val) {
var i1 = new Image(1, 1);
var link = getABC('creosote');
link += "&category=" + String(cat);
link += "&event_type=" + String(type);
link += "&event_value=" + String(val);
i1.src = link;
}
The spyOn() below causes the "new Image" to be fed the fake code from the test
the spyOn code returns an object that only has a src property
As the variable "hook" is scoped to be visible in the fake code in the SpyOn and also later after the "reportABCEvent" is called
describe("Alphabetic.ads", function() {
it("ABC events create an image request", function() {
var hook={};
spyOn(window, 'Image').andCallFake( function(x,y) {
hook={ src: {} }
return hook;
}
);
reportABCEvent('testa', 'testb', 'testc');
expect(hook.src).
toEqual('[zubzub]&arg1=testa&arg2=testb&event_value=testc');
});
This is for jasmine 1.3 but might work on 2.0 if the "andCallFake" is altered to
the 2.0 name

I'm using a kendo grid and therefore can't change the implementation to a getter method but I want to test around this (mocking the grid) and not test the grid itself. I was using a spy object but this doesn't support property mocking so I do this:
this.$scope.ticketsGrid = {
showColumn: jasmine.createSpy('showColumn'),
hideColumn: jasmine.createSpy('hideColumn'),
select: jasmine.createSpy('select'),
dataItem: jasmine.createSpy('dataItem'),
_data: []
}
It's a bit long winded but it works a treat

You can also use
jasmin.creatSpyObj('ObjectName', [methodNames...], {prop1:propvalue, prop2:provalue2})

I'm a bit late to the party here i know but,
You could directly access the calls object, which can give you the variables for each call
expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)

You can not mock variable but you can create getter function for it and mock that method in your spec file.

Related

Monkey-patching jest object for all test files

How can I monkey-patch some methods in the global jest object for all test files at once? I don't want to add any extra code to my test files, it has to be done somewhere in setup and it can be an ugly hack.
I tried doing that from a custom environment, setupFiles and setupFilesAfterEnv, but it looks like they all get a different instance of jest object and my changes aren't visible in test files.
Disclaimer: I know that it's a bad thing to do but I need it for some one-time benchmarking only and it's the easiest solution that gets the job done.
I got this working! You're right, Jest does re-construct the global jest object for every test case, but you can override the function it uses to do that. In jest.config.js, set globalSetup to something like <rootDir>/jest-global-setup.js. Then, in jest-global-setup.js, add this:
const jestRuntime = require('jest-runtime');
const { _createJestObjectFor } = jestRuntime.prototype;
jestRuntime.prototype._createJestObjectFor = function(...args) {
// Call the original function to create a normal jest object.
const jestObject = _createJestObjectFor.apply(this, args);
// Apply your changes.
jestObject.isMonkeyPatched = true;
// Return the patched object.
return jestObject;
}
// Jest expects to find a function of some sort as well,
// but we don't need it for this example.
module.exports = function() { /* do nothing */ }

Can I call behaviors methods directly without eventproxy?

I'm searching for alternative ways to call a method defined in Marionette's behaviors from inside a view.
For sure there is the eventproxy but maybe it's more intuitive to call the method directly like:
view.behaviorsMethod();
I could assign it like:
view.method = behavior.method;
I could check for reassignment because it'll maybe lead to unexpected results for others:
view.method = (view.method !== undefined ? view.method : behavior.method);
But this doesn't seem to be an elegant way.
The answer to your question is you can not directly do so, but there is always a way.
you can do it using _.invoke(this._behaviors, 'yourMethodName') but I will discourage using it
since
_behaviors is a private variable of the Marionette.View class and it's name can be changed or it can be dropped in upcoming versions
You will have to set context for the method as _.invoke will not set
the context of the method to proper this.
if you can set the context properly then this will work for you.
as suggested by #ThePaxBisonica in comment
I will suggest you to go with a mixin pattern from which you can extend both your behavior and view and you will not have to set any context and do not have to worry about the _behavior private variable
as
var mixin = {
behaviorMethodWhichYouWantToCallFromView: function(){
alert("mixin method");
}
}
var behavior = mn.behavior.extend(_.extend(mixin, {
//actual behavior code remove the method as behavior will get it from mixin
}))
var view = mn.view.extend(_.extend(mixin, {
//actual view code remove the method as behavior will get it from mixin
}))
Hope it helps.
I know this is bit long approach.

Replacing anonynomous function while keeping all others from module

I'm fairly new to node.js, prototypical inheritance and the CommonJS module patterns.
Maybe this question was answered a million times, but I couldn't find it, so even a link to the answer is considered an answer.
I have to wrap a module that has both named and unnamed functions, like this:
// a.js
function a(data) {
console.log(data, 'A')
}
function b() {
a('B');
}
module.exports = a;
module.exports.b = b;
Given OOP background I would like to somehow 'inherit' all the functions of the module while I want to override the anonymous function (I'd like to add some fields to the data).
It is very important that after overriding function a in the new module function b should use the overridden method and not the original one.
// 'inherited.js'
var a = require('./a');
function overriddenA(data) {
data.myAddedValue = 'an important addition';
a(data);
}
// I would like to export all other functions and properties of the original module
[magic that overrides the anonymous function while keeping all other functions as they are]
From where I use it should look like this:
var decoratedA = require('./inherited');
decoratedA('stuff'); // it calls overridden function
decoratedA.b(); // it calls the original a.b() which in turn calls the overridden function
Solved our original problem
Check out this: https://stackoverflow.com/a/31459267/2018771 - still if you have any comment on the abstract problem, please answer the question. We are curious :).
I would like to somehow 'inherit' all the functions of the module while I want to override the anonymous function
Wanna use some dark magic? Then __proto__ is the way to go:
var a = require('./a');
function overriddenA(data) {
data.myAddedValue = 'an important addition';
a(data);
}
overriddenA.__proto__ = a;
module.exports = overriddenA;
The cleaner method, without actual inheritance, would be to just copy over all properties from a to overriddenA. You can use Object.assign (or a shim), _.extend, or a simple for in loop for that.
Meanwhile we have solved our original problem which was adding a special header to every calls of request library. In that implementation there were functions like get(), put(), post(), etc. that used one function: function request(...) which was exported module.exports = request
My understanding was that I had to replace that request(...) with our own, adding our header and then calling the original function.
But we were lucky because request(...) returned an object which we could modify according our needs:
Request.prototype.__originalInit = Request.prototype.init;
requestPromise.Request.prototype.init = function(options){
console.log('adding our stuff');
this.__originalInit(options);
};
So this solved the problem for us, but not the original question.

Javascript function assigned to object cannot access inherited method

I'm no doubt doing something dumb here, but the following code results in an
error "this.getString is not a function."
This occurs because when unrelatedInstance calls stringGetter, "this" in showCombinedStrings() has the value of Unrelated....which actually seems fair enough, but how could this be set up so that it would work?
function BaseStringGetter() {
this.getString = function () {
return 'this is from BaseStringGetter';
}
}
function DerivedStringGetter() {
this.showCombinedStrings = function () {
console.log( 'this is from DerivedStringGetter and... ' + this.getString() );
}
}
DerivedStringGetter.prototype = new BaseStringGetter();
var stringGetterInstance = new DerivedStringGetter();
function Unrelated() {};
var unrelatedInstance = new Unrelated();
unrelatedInstance.stringGetter = stringGetterInstance.showCombinedStrings;
unrelatedInstance.stringGetter();
One option is this:
unrelatedInstance.stringGetter =
stringGetterInstance.showCombinedStrings.bind(stringGetterInstance);
unrelatedInstance.stringGetter();
Here, you're using Function.prototype.bind() to make this inside of unrelatedInstance.stringGetter() always refer back to stringGetterInstance.
The problem is how you are calling unrelatedInstance.stringGetter();
Even though stringGetter refers to the showCombinedStrings function, this inside showCombinedStrings now refers to the unrelatedInstance instance which does not have the toString() property that is why the error.
Demo: Fiddle
Here the value of this is printed as Unrelated {stringGetter: function} which is not an DerivedStringGetter instance
One easy solution is to use .bind() to give a custom execution context to unrelatedInstance.stringGetter like
unrelatedInstance.stringGetter = stringGetterInstance.showCombinedStrings.bind(stringGetterInstance);
Demo: Fiddle
Now even if you call unrelatedInstance.stringGetter(), this inside showCombinedStrings will refer to the stringGetterInstance instance.
When you call a function on an object, this will refer to the object the function is invoked on even if the function was originally defined elsewhere.
When you call unrelatedInstance.stringGetter();, this inside the function will now refer to unrelatedInstance which doesn't have getString(). The MDN this reference page has more info.
You could do something like this to preserve the original context:
unrelatedInstance.stringGetter = function() {
return stringGetterInstance.showCombinedStrings();
}
Edit: I left out bind that the other answers have now mentioned since it doesn't exist in IE8 but you'd be fine using that if you have a shim or don't care about old browsers.

Does Javascript have something like Ruby's method_missing feature?

In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
Can Javascript do the same kind of thing ?
method_missing does not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__ special method.
The __noSuchMethod__ proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
The way forward for JavaScript is the Proxy mechanism (also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute access than to Ruby's method_missing.
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
You can use the Proxy class.
var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
About
You would use p in place of myObj.
You should be careful with get because it intercepts all attribute requests of p. So, p.specialPants() would result in an error because specialPants returns a string and not a function.
What's really going on with unknownMethod is equivalent to the following:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
Bonus
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
I've created a library for javascript that let you use method_missing in javascript: https://github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
I can think of two ways of doing this:
Prototypes
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
Copying methods
This method is probably more expensive but seems more elegant to me and will also work on code that is using this (eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
Here's the mods:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
I was actually anticipating having to use bind in there somewhere but it appears not to be necessary.
Not to my knowledge, but you can simulate it by initializing the function to null at first and then replacing the implementation later.
var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition
// ...
foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
The only downside is that if you do use foo before it's defined, you'll get an error for trying to call null as though it were a function, rather than a more descriptive error message. But you would expect to get some error message for using a function before it's defined.

Categories