Calling replaceGetter invokes the original function
egmacke opened this issue · comments
Describe the bug
Using sinon.replaceGetter
correctly replaces the getter, but in the process it appears to invoke the original get
function.
This causes issues when the getter is non-trivial and relies on class state which may not be correctly setup in a test environment.
To Reproduce
Steps to reproduce the behavior:
- Define getter with side effects
- call
sinon.replaceGetter
- Observe side effects happening
Reproduction here: https://replit.com/@egmacke/Sinon-replaceGetter-bug
Expected behavior
Expect that the getter is replaces without invoking the original implementation.
Context (please complete the following information):
- Sinon version : 17.0.1
- Runtime: node
- Output of
npx envinfo --browsers --binaries
:
~/Sinon-replaceGetter-bug$ npx envinfo --browsers --binaries
Binaries:
Node: 18.12.1 - /nix/store/dj805sw07vvpbxx39c8g67x8qddg0ikw-nodejs-18.12.1/bin/node
Yarn: 1.22.19 - /nix/store/zdcnqq55qi214j46zgw2qa1jp4dpdf2m-yarn-1.22.19/bin/yarn
npm: 8.19.2 - /nix/store/dj805sw07vvpbxx39c8g67x8qddg0ikw-nodejs-18.12.1/bin/npm
Good repro! Thanks.
Do you want to take a stab at it? I am guessing it is not a very hard fix. We have good test coverage.
For this given replit if you print the source for the object whose getter is being replaced(using descriptor.get.toString()), the function has changed to the replacement. The console output you see is coming from line 246 in the function getFakeRestorers when the getter is executed to potentially set the field if forceAssignment is true.
Logically it needs to execute this way because restorers need to resolve the getter to be able to restore the original value you had in that function.
A good example is the test starting on line 1119 in sandbox-test where you have a variable that is being get and set by an object. Even if you try to delay the execution of the getter it will return you the most recent value of that variable, not the past value that needs to be restored. In the case that you just have simple output like a constant ('original' or 'fake') it's obviously different because you could get the descriptor for foo and then use the getter in that descriptor.
From my understanding getters should be pretty simple and should avoid side effects to begin with, so the solution in my opinion here should be to avoid side effects in your getters.