sinonjs / sinon

Test spies, stubs and mocks for JavaScript.
https://sinonjs.org/
Other
9.64k stars 772 forks source link

Cannot stub static methods of class #2511

Closed hustlerman closed 1 year ago

hustlerman commented 1 year ago

Describe the bug A clear and concise description of what the bug is.

Attempting to stub a static class method causes an error: sandbox.stub(GameSession, 'create').callsFake(async () => { return "bye" })

export class GameSession { static async create(): String { return "hi" }

Test command and output:

TS_NODE_FILES=true mocha --require ts-node/register "test/**/*.ts"

  GameSessionControlle
    1) should create a game successfully

  0 passing (7ms)
  1 failing

  1) GameSessionController
       should create a game successfully:
     Error: Trying to stub property 'create' of undefined

Expected behavior A clear and concise description of what you expected to happen.

Method to be stubbed successfully and return 'bye' when called

Context (please complete the following information):

Node: 18.13.0 Sinon: 15.0.4 @types/sinon: 10.0.14

fatso83 commented 1 year ago

Sinon clearly has no issue stubbing static methods (see this bug template I use on Runkit, for instance, as it does just that), so there's probably some details in your example that is off. Typescript issues is outside the scope of what we care about, but the repro seems small enough that I'll see what this is about 👍

fatso83 commented 1 year ago

I could not reproduce this in normal EcmaScript:

repro.mjs

import sinon from "sinon";

class GameSession {
  static async create() {
    return "hi";
  }
}

sinon.stub(GameSession, "create").callsFake(async () => {
  return "bye";
});

const result = await GameSession.create();
console.log(result);

Running it

$ node repro.mjs
bye
fatso83 commented 1 year ago

Unsurprisingly, I cannot reproduce using Typescript either.

$  TS_NODE_FILES=true npx mocha --require ts-node/register repro.test.ts

  ✔ this should not fail

  1 passing (3ms)

Here's the reproducible setup:

$ command cat package.json tsconfig.json  repro.test.ts
{
  "dependencies": {
    "mocha": "^10.2.0",
    "sinon": "^15.0.4",
    "ts-node": "^10.9.1"
  },
  "devDependencies": {
    "@types/mocha": "^10.0.1",
    "@types/sinon": "^10.0.14",
    "i": "^0.3.7"
  }
}
{
  "compilerOptions": {
    "esModuleInterop": true,
    "strict": true,
    "typeRoots": [
      "./node_modules/@types"
    ]
  }
}
import assert from "assert";
import sinon from "sinon";

class GameSession {
  static async create(): Promise<String>  {
    return "hi";
  }
}

it("this should not fail", async () => {
  const sandbox = sinon.createSandbox();

  sandbox.stub(GameSession, "create").callsFake(async () => {
    return "bye";
  });

  const result = await GameSession.create();

  assert.equal("bye", result);
});
fatso83 commented 1 year ago

The most likely issue with this (just a guess, as your example is not reproducible and hence is failing to recreate your environment this is failing in) is that you somehow fail to import or export the module correctly:

 Error: Trying to stub property 'create' of undefined

This means GameSession is undefined, which is not a problem with Sinon.