denoland / std

The Deno Standard Library
https://jsr.io/@std
MIT License
3.16k stars 619 forks source link

testing/mock.ts: Add support for stubbing properties #5848

Open KyleJune opened 2 months ago

KyleJune commented 2 months ago

Is your feature request related to a problem? Please describe.

Currently the mocking tool doesn't support stubbing getter/setter properties. It only allows stubbing functions. Other mocking tools like sinon do support it.

It's probably not a common need and would just be a nice to have. I'm creating this issue based on a comment someone made in the discord's #dev-std channel where they were requesting a way to stub a getter.

Describe the solution you'd like

The ideal solution to this problem to me would be to add an additional call signature to the stub function, that allows you to pass a property descriptor instead of a function as the third argument.

stub<Self, Prop extends keyof Self>(self: Self, property: Prop, descriptor: Omit<PropertyDescriptor, 'configurable'>)

It would need to omit the configurable property since a property must be configurable for it to be able to be restored to it's original value.

If a get or set function are set on the descriptor, they should be wrapped in a spy so that you can make assertions about them being called. You would just need a way to access them. Maybe the value returned by the stub function could have get and set properties on it so that people can access the spys by doing myStub.get or myStub.set where myStub is the value returned by the stub function when you call it with a property descriptor instead of a function.

For more information about property descriptors, see Object.defineProperty documentation on mdn.

Describe alternatives you've considered

With sinon.js, you can mock properties by creating a stub then calling the get, set, or value functions on the stub object. That can be seen at the end of the following page. I'm guessing it's at the end because it's uncommon to use it, but it would be good to have for when people want to do this.

https://sinonjs.org/releases/latest/stubs/

Here is an example of stubbing a getter with sinon.

const sinon = require("sinon");
const referee = require("@sinonjs/referee");
const assert = referee.assert;

describe("stub", function () {
    it("should replace getter", function () {
        const myObj = {
            prop: "foo",
        };

        sinon.stub(myObj, "prop").get(function getterFn() {
            return "bar";
        });

        assert.equals(myObj.prop, "bar");
    });
});
KyleJune commented 7 hours ago

I added some comments to the recent PR working on this about some recommendations for improving this to make it easier to stub properties and to add support for spying on properties.

https://github.com/denoland/std/pull/6128