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

159,506

Solution 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();

Solution 2

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.

Solution 3

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.

Solution 4

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

Solution 5

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();
Share:
159,506
Shuping
Author by

Shuping

This guy is too lazy to leave a message

Updated on July 08, 2022

Comments

  • Shuping
    Shuping almost 2 years

    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);
    
  • Shuping
    Shuping over 10 years
    using spyOn explicitly indicates that I want mock something, while I directly set the property implicitly indicates that I want mock something, and I am not sure somebody else will understand that I am mocking something when he is reading the code. Other case is that I don't want change the inner behavior of the object, for example if I change the length property for an array, the array is trimmed, so a mock will be better
  • Les
    Les over 9 years
    @Shuping, in this case, you'd not be mocking. You'd be stubbing which is perfectly okay. You'd be "changing the behaviour" only inside the the test which is what you were trying to achieve with the spyOn.
  • sennett
    sennett about 9 years
    Possibly you want to spy on the runtime object prototype to make sure the property will exist at runtime. Jasmine spyOn fails the test if the property does not exist.
  • Chris Sattinger
    Chris Sattinger almost 8 years
    One example is to set or delete window.sessionStorage: TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
  • Tom
    Tom over 7 years
    This is the right answer. You should be able to do this: myObj.valueA = 1;
  • Ka Mok
    Ka Mok about 6 years
    How do I do this if valueA is a Observable or Subject? I'm getting Property valueA does not have access type get
  • Juan
    Juan about 6 years
    That probably means you can't use it on that property. spyOnProperty is using getOwnPropertyDescriptor and checking if the access type get|set for that property descriptor exists. The error you are getting is because the property descriptor is not set or get for the property you are trying to spy on. Jasmine does this: const descriptor = Object.getOwnPropertyDescriptor ... if(!descriptor[accessType]) { // error is thrown }
  • ccprog
    ccprog about 6 years
    Or, if the getters are part of the class under test, the stubs could be injected in a subclass.
  • Renaud
    Renaud almost 6 years
    It is not always possible to reassign an object property in javascript
  • pradeep gowda
    pradeep gowda over 5 years
    @Paul Harris, What if the value is of type observable? if i want to return observable with value, then?
  • Dominik
    Dominik about 4 years
    So, there is no way to spy a property without explicit getter and setter methods? To spy is a property used.
  • Juan
    Juan about 4 years
    @Dominik Here I wrote all the under the hood stuff related to this in a longer article: medium.com/@juanlizarazo/…
  • Felix
    Felix about 4 years
    Is this possible in Jest too?
  • Mathieu
    Mathieu about 4 years
    Sorry but it's not clear for me how this is a test if you explicitly cast the output to be 1 and test it to be 1?
  • Juan
    Juan about 4 years
    @Mathieu it is not a good assertion because it just asserts what we already know from what we told the spy to do, but it is just what they were asking and their very same question code snippet ¯_(ツ)_/¯ The point of their question was how to spy on a property and not so much their specific example.
  • Mathieu
    Mathieu about 4 years
    @Juan, I was looking for that kind of test. I'm desperatly trying to read a getter in my Jasmine test and it always return undefined...
  • Juan
    Juan about 4 years
    @Mathieu that's the correct way to mock it. Here just the example was that you would not assert on the spy itself, as that's silly. You would assert something affected by your spy, some outcome or that the spy itself is called. For your specific issue, as undefined might be caused by something else, just ask / open a new question! someone will help :)