How to spyOn a value property (rather than a method) with Jasmine
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.
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();
Comments
-
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 over 10 yearsusing
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 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 about 9 yearsPossibly 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 almost 8 yearsOne example is to set or delete window.sessionStorage:
TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
-
Tom over 7 yearsThis is the right answer. You should be able to do this:
myObj.valueA = 1;
-
Ka Mok about 6 yearsHow do I do this if
valueA
is aObservable
orSubject
? I'm gettingProperty valueA does not have access type get
-
Juan about 6 yearsThat 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 about 6 yearsOr, if the getters are part of the class under test, the stubs could be injected in a subclass.
-
Renaud almost 6 yearsIt is not always possible to reassign an object property in javascript
-
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 about 4 yearsSo, there is no way to spy a property without explicit getter and setter methods? To spy is a property used.
-
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 about 4 yearsIs this possible in Jest too?
-
Mathieu about 4 yearsSorry 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 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 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 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 :)