How can I spy on a getter property using jasmine?

JavascriptJasmine

Javascript Problem Overview


How can I spy on a getter property using jasmine?

var o = { get foo() {}, };

spyOn(o, 'foo').and.returnValue('bar'); // Doesn't work.

This also does not work AFAICT:

spyOn(Object.getOwnPropertyDescriptor(o, 'foo'), 'get').and.returnValue('bar');

Javascript Solutions


Solution 1 - Javascript

Since Jasmine 2.6, this has been possible with spyOnProperty. To spy on the accessors for the foo property, do:

spyOnProperty(o, 'foo')

This allows you to replace the set and/or get accessor functions for an accessor property with a spy function. You can specify or set or get only as a third argument:

spyOnProperty(o, 'foo', 'get')

If you are stuck using an earlier version and cannot upgrade for some reason, you may be able to merge the pull request that added this feature into your local copy of the code.

Solution 2 - Javascript

In February 2017, they merged a PR adding this feature, they released it 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

Answering the original question, with jasmine 2.6.1, you would:

var o = { get foo() {} };
spyOnProperty(o, 'foo', 'get').and.returnValue('bar');

Solution 3 - Javascript

You can do this if you are unable to use the latest jasmine (2.6.1)

const getSpy = jasmine.createSpy().and.returnValue('bar')
Object.defineProperty(o, 'foo', { get: getSpy });

Documentation for defineProperty, here

Solution 4 - Javascript

I took inspiration from @apsillers response and wrote the following helper (requires the prop to be configurable as mentioned above)

let activeSpies = [];
let handlerInstalled = false;

function afterHandler() {
    activeSpies.forEach(({ obj, prop, descriptor }) => Object.defineProperty(obj, prop, descriptor));
    activeSpies = [];
}


export function spyOnGetter(obj, prop) {
    const env = jasmine.getEnv();
    const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
    const spy = jasmine.createSpy(`${prop} spy`);
    const copy = Object.assign({}, descriptor, { get: spy });
    Object.defineProperty(obj, prop, copy);
    activeSpies.push({
        obj,
        prop,
        descriptor,
    });

    if (!handlerInstalled) {
        handlerInstalled = true;
        env.afterEach(() => afterHandler());
    }
    return spy;
}

And it can be used like so:

import { spyOnGetter } from spyExtra;
it('tests the thing', () => {
    spyOnGetter(myObj, 'myProp').and.returnValue(42);
    expect(myObj.myProp).toBe(42);
});

Hope it's useful!

Solution 5 - Javascript

I think the best way is to use spyOnProperty. It expects 3 properties and you need to pass get or set as third property.

spyOnProperty(o, 'foo', 'get').and.returnValue('bar');

Solution 6 - Javascript

I don't believe you can spy on getters. The point of a getter is that it acts exactly like a property, so how would jasmine be able to spy on it when it is never called like a function but rather is accessed like a property.

As a workaround, you could have your getter call another function and spy on that instead.

var o = {
     _foo: function(){
        return 'foo'; 
     }, 
     get foo(){
        return this._foo();
     }
};

spyOn(o, '_foo').and.returnValue('bar'); 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBen AstonView Question on Stackoverflow
Solution 1 - JavascriptapsillersView Answer on Stackoverflow
Solution 2 - JavascriptJuanView Answer on Stackoverflow
Solution 3 - JavascriptGeorge Valentin IurieţView Answer on Stackoverflow
Solution 4 - JavascriptAnilRedshiftView Answer on Stackoverflow
Solution 5 - JavascriptAniruddha DasView Answer on Stackoverflow
Solution 6 - JavascriptTwitchBronBronView Answer on Stackoverflow