jest icon indicating copy to clipboard operation
jest copied to clipboard

fix: mocking of getters/setters on automatically mocked classes

Open staplespeter opened this issue 3 years ago • 13 comments

This is a new pull request to complete #13398.

staplespeter avatar Oct 17 '22 11:10 staplespeter

I added a test for module exports that are imported via getters, based upon @SimenB code.

'use strict';
Object.defineProperty(exports, '__esModule', {
  value: true,
});
function _export(target, all) {
  for (const name in all)
    Object.defineProperty(target, name, {
      enumerable: true,
      get: all[name],
      configurable: true,
    });
}
_export(exports, {
  f1: () => f1,
  f2: () => f2,
});
const f1 = () => {
  console.log('Im f1 calling f2');
  (0, exports.f2)();
};
const f2 = () => console.log('Im f2');

The test,'can mock a function export', is here. The restoring of the original property fails and is not tested. Can anyone tell me how to make this a valid test?

staplespeter avatar Oct 17 '22 12:10 staplespeter

The test,'can mock a function export', is here. The restoring of the original property fails and is not tested. Can anyone tell me how to make this a valid test?

@mrazauskas any idea?

Did it work before? If not it shouldn't block

SimenB avatar Oct 17 '22 13:10 SimenB

Looks complicated. Maybe mockRestore() is buggy? Only guessing (;

mrazauskas avatar Oct 17 '22 13:10 mrazauskas

Are this new changes/tests also covering https://github.com/facebook/jest/issues/13466 ?

nuragic avatar Oct 18 '22 13:10 nuragic

Should add a test for it at least

SimenB avatar Oct 18 '22 13:10 SimenB

I need to make a release for Node 19 today btw, so I might have to revert (again 😅)

SimenB avatar Oct 18 '22 13:10 SimenB

I reverted the original PR in #13472 (but kept a bunch of tests). I'll merge in main here

SimenB avatar Oct 18 '22 15:10 SimenB

Looks complicated. Maybe mockRestore() is buggy? Only guessing (;

It was the defineProperty() during the restore that resulted in the property being undefined. Not sure why as the defineProperty() during the test worked.

staplespeter avatar Oct 18 '22 20:10 staplespeter

@SimenB I've reapplied the automatic mocking tests that were removed, and reapplied the original spyOn method structures. Apologies again for the hassle caused.

staplespeter avatar Oct 18 '22 23:10 staplespeter

The dispatchEvent bug #13466 was caused by the window.dispatchEvent property not having a [public at least??] descriptor and not being visible in the debugger, though window['dispatchEvent'] returns the function definition. The latter was used in he original code whilst my well-intended refactoring relied on property descriptors. It became a little complicated to try to fall back to the property read if the descriptor was not present, and the original code seems to cause less issues.

That being said the original code const original = object[methodKey]; fails to get a function definition in spyOn() in the test below - undefined is returned instead and spyOn() throws an exception. This also occurred in the original code when automatically mocking a getter/setter, which was one of the reasons to try to use property descriptors exclusively in spyOn(), as property reads seemed unreliable.

In the test below, in my version of spyOn() the property descriptor is returned and the mock applied. Calling defineProperty() to restore the original accessor property definition resulted in an undefined property, which then caused the assertion to fail. Alas, as mentioned, there exist objects that have properties with no available property descriptors (??).

It seems there are edge cases for either implementation so detecting both may be necessary at some point. As use of property descriptors seems to cause more real-world hassle I'll stick with the original code for now.

It would be nice to know why these anomalies occur, if anyone has the knowledge.

it('can mock a function export', () => {
    const exports = {
      __esModule: true,
      fn: () => {},
    };
    Object.defineProperty(exports, 'fn', {
      configurable: true,
      enumerable: true,
      get: () => {
        () => 'testFunction';
      },
    });

    jest.spyOn(exports, 'fn').mockImplementation(() => 'mockTestFunction');
    expect(exports.fn()).toBe('mockTestFunction');

    // mockFn.mockRestore();
    // expect(exports.fn()).toBe('testFunction');
  });

staplespeter avatar Oct 19 '22 00:10 staplespeter

@SimenB I've reapplied the automatic mocking tests that were removed, and reapplied the original spyOn method structures. Apologies again for the hassle caused.

Thanks! And don't worry about the regressions too much - reverting is easy enough 🙂 The real issue is our tests not covering the super valid use cases people have, not your changes breaking them 👍

SimenB avatar Oct 19 '22 07:10 SimenB

@SimenB thanks, the comment is appreciated.

staplespeter avatar Oct 19 '22 12:10 staplespeter

This PR is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 30 days.

github-actions[bot] avatar Jan 17 '23 12:01 github-actions[bot]

This PR was closed because it has been stalled for 30 days with no activity. Please open a new PR if the issue is still relevant, linking to this one.

github-actions[bot] avatar Feb 16 '23 12:02 github-actions[bot]

This PR was closed because it has been stalled for 30 days with no activity. Please open a new PR if the issue is still relevant, linking to this one.

github-actions[bot] avatar Feb 16 '23 12:02 github-actions[bot]

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

github-actions[bot] avatar Mar 19 '23 00:03 github-actions[bot]