sinonjs/sinon

"Attempted to wrap undefined property **mySpiedMethod** as function" in a function exported as a getter

Xavier-Redondo opened this issue · 3 comments

Describe the bug

I want to reset an spy on a function that typescript exports as a getter function. Instead of having the expected behaviour i get: "Attempted to wrap undefined property executeInsert as function"

I would like to know which is the canonical/official way to reset/restore a getter function from a library.

As a workaround i am using import-fresh to re-import from a clean cache every time the library myLibrary and then the tests works, but i would expect that this would not be required .

To Reproduce

  1. To be precise, the spied function that is giving me issues is exported by typescript transpiling like this:
Object.defineProperty(exports, "executeInsert", { enumerable: true, get: function () { return sql_query_by_id_1.executeInsert; } });
  1. I have a method that creates a stub and a spies. To simplify i put here only the code of the affected spy, which is a function exposed as a getter by typescript inside a custom library here called myLibrary:
export function getTransactionalInsertStub(
  sinon: SinonSandbox
) {

  const executeInsertSpy = sinon.spy(myLibrary, 'executeInsert', ['get']) as any;

  return {
    executeInsertSpy
  };
}

3.In the test i do the following (what the test does it is not that important, since the test works fine when executed individually):

 t1.test('should rollback and throw an error if an unexpected error happens', async expect => {
      expect.plan(1);
      const { connectionPoolStub, requestUser, rollbackStub } = getTransactionalInsertStub(
        sandbox,
        undefined,
        new Error('SQL error while doing an insert: Nobody expects the SQL error!')
      );

      await expect.rejects(
        createUserSQL(connectionPoolStub, requestUser),
        internalServerError('createUserSQL: Unknown data base error')
      );

      expect.ok(rollbackStub.calledOnce);
    });
  1. Now, i can have one test and it works like a charm, but the moment i have two tests calling the stub generator method getTransactionalInsertStub, the second test fails with the message "Attempted to wrap undefined property executeInsert as function"

This does not happen with other stuff that i have mocked/stubbed, only with the spies against my getters (i have not put all the code to make the issue as short as possible).

  1. I have tried reset, restore, both against sinon and against sandbox, to no avail. Example of a hook to clear:
  t.afterEach(() => {
    sandbox.restore();
    sandbox.reset();
  });

Expected behavior

I would expect the spy restored/reset and the second test running successfully.

Screenshots
If applicable, add screenshots to help explain your problem.

Context (please complete the following information):

  • Library version: 15.2.0
  • Environment: node v18.16.1
  • Other libraries you are using: If relevant i am using tap v16.3.7, typescript v4.9.5

Additional context

  1. The tests that i have put as example are totally fine and each test works perfectly fine if run by itself. Is at the moment when the spy should be reset that the second test fails.

  2. it is not relevant whether is best to use directly sinon or a sandbox, i have tested both and none work.

  3. the same with reset or restore, it seems that for spies reset must be used but in any case if the issue is that i am not using the right method any hint would be appreciated as well.

I think you might have stumbled upon a bug. Spying/stubbing propertie/synthetic fields is relatively rare, so maybe not many actually tried all that hard, and I am guessing the tests simply test that creation and restore works, but haven't touched the same function twice.

Hi, the example code is not actually reproducible in any sense. The return values do not match what your function created, using spy instead of stub, etc.

In any case, I tried manually reproducing it in this RunKit book: https://runkit.com/fatso83/649df02251b50f0008d1e3a6

  1. I was never able to get your error message. I got others (like trying to wrap an already wrapped ... )
  2. Your Object.defineProperty line must be wrong, because it lacks the configure: true bit, which is what allows Sinon to mutate the field. If you do not provide that, it will fail early.
  3. I was able to verify that restoring the sandbox (Sinon uses a sandbox internally) will NOT restore the stubs or spies correctly. You presently need to manually restore them by calling each single stub/spy ...

So I found a bug, but not the one you seem to indicate. Can you please provide a minimally reproducible example that shows the issue? Have a look at my code in the RunKit for inspiration.

Made #2534. Closing due to lack of response