Search code examples
jestjssinon

Sinon stub replaces class property for whole test file instead of describe block


I'm trying to replace a static method of a class so that it returns custom value for testing purposes.

I have multiple test (describe) blocks in my file, and my goal is to replace the static method only in one of the describe blocks.

The problem is that the method is replaced for all the tests, even though I have a teardown method that is replacing the static method with the original method.

Some code:

class Cat {
    static sound() {
        return "Meow";
    }
}

module.exports = {
    Cat
}
const sinon = require("sinon");

const { Cat } = require("./myClass");

describe("Main tests", () => {
    describe("Test Cat", () => {
        it("Meows", () => {
            expect(Cat.sound()).toBe("Meow")
        })
    })

    describe("Test Dog", () => {
        const stub = sinon
            .stub(Cat, "sound")
            .callsFake(() => "Woof");

        afterAll(() => {
            stub.restore();
        });

        it("Barks", () => {
            expect(Cat.sound()).toBe("Woof")
        })
    })
})

Test results - the unreplaced test case is failing:

 FAIL  ./index.test.js
  Main tests
    Test Cat
      ✕ Meows (6ms)
    Test Dog
      ✓ Barks

  ● Main tests › Test Cat › Meows

    expect(received).toBe(expected) // Object.is equality

    Expected: "Meow"
    Received: "Woof"

       7 |     describe("Test Cat", () => {
       8 |         it("Meows", () => {
    >  9 |             expect(Cat.sound()).toBe("Meow")
         |                                 ^
      10 |         })
      11 |     })
      12 | 

Is there a way how to prevent this? I tried using createSandbox:

const sandbox = sinon.createSandbox()
        const stub = sandbox
            .stub(Cat, "sound") // etc

but it's the same thing.

Any help would be appreciated.


Solution

  • This task can be done easily with jestjs only (without sinon).

    Just use jest.spyOb function to spy sound function, and you can mock the result of this function:

    const { Cat } = require('./myClass');
    
    describe('Main tests', () => {
      beforeEach(() => {
        jest.spyOn(Cat, 'sound');
      });
    
      afterEach(() => {
        jest.resetAllMocks();
      });
    
      describe('Test Cat', () => {
        it('Meows', () => {
          // Don't mock, just call actual logic
          expect(Cat.sound()).toBe('Meow');
        });
      });
    
      describe('Test Dog', () => {
        it('Barks', () => {
          Cat.sound.mockReturnValue('Woof'); // mock return value of `sound()`
    
          expect(Cat.sound()).toBe('Woof');
        });
      });
    });