Skip to content

Commit

Permalink
fix(jest-mock): clear mock when jest.restoreAllMocks() is called (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mrazauskas committed Feb 8, 2023
1 parent ca8acf7 commit 66fb417
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,8 @@

### Fixes

- `[jest-mock]` Clear mock state when `jest.restoreAllMocks()` is called ([#13867](https://github.com/facebook/jest/pull/13867))

### Chore & Maintenance

### Performance
Expand Down
130 changes: 111 additions & 19 deletions packages/jest-mock/src/__tests__/index.test.ts
Expand Up @@ -1312,6 +1312,36 @@ describe('moduleMocker', () => {
);
});

it('supports restoring a spy', () => {
let methodOneCalls = 0;
const obj = {
methodOne() {
methodOneCalls++;
},
};

const spy1 = moduleMocker.spyOn(obj, 'methodOne');

obj.methodOne();

// The spy and the original function got called.
expect(methodOneCalls).toBe(1);
expect(spy1.mock.calls).toHaveLength(1);

expect(moduleMocker.isMockFunction(obj.methodOne)).toBe(true);

spy1.mockRestore();

// After restoring the spy, the method is not mock function.
expect(moduleMocker.isMockFunction(obj.methodOne)).toBe(false);

obj.methodOne();

// After restoring the spy only the real method bumps its call count, not the spy.
expect(methodOneCalls).toBe(2);
expect(spy1.mock.calls).toHaveLength(0);
});

it('supports restoring all spies', () => {
let methodOneCalls = 0;
let methodTwoCalls = 0;
Expand All @@ -1327,25 +1357,32 @@ describe('moduleMocker', () => {
const spy1 = moduleMocker.spyOn(obj, 'methodOne');
const spy2 = moduleMocker.spyOn(obj, 'methodTwo');

// First, we call with the spies: both spies and both original functions
// should be called.
obj.methodOne();
obj.methodTwo();

// Both spies and both original functions got called.
expect(methodOneCalls).toBe(1);
expect(methodTwoCalls).toBe(1);
expect(spy1.mock.calls).toHaveLength(1);
expect(spy2.mock.calls).toHaveLength(1);

expect(moduleMocker.isMockFunction(obj.methodOne)).toBe(true);
expect(moduleMocker.isMockFunction(obj.methodTwo)).toBe(true);

moduleMocker.restoreAllMocks();

// Then, after resetting all mocks, we call methods again. Only the real
// methods should bump their count, not the spies.
// After restoring all mocks, the methods are not mock functions.
expect(moduleMocker.isMockFunction(obj.methodOne)).toBe(false);
expect(moduleMocker.isMockFunction(obj.methodTwo)).toBe(false);

obj.methodOne();
obj.methodTwo();

// After restoring all mocks only the real methods bump their count, not the spies.
expect(methodOneCalls).toBe(2);
expect(methodTwoCalls).toBe(2);
expect(spy1.mock.calls).toHaveLength(1);
expect(spy2.mock.calls).toHaveLength(1);
expect(spy1.mock.calls).toHaveLength(0);
expect(spy2.mock.calls).toHaveLength(0);
});

it('should work with getters', () => {
Expand Down Expand Up @@ -1482,6 +1519,33 @@ describe('moduleMocker', () => {
);
});

it('supports restoring a spy', () => {
let methodOneCalls = 0;
const obj = {
get methodOne() {
return function () {
methodOneCalls++;
};
},
};

const spy1 = moduleMocker.spyOn(obj, 'methodOne', 'get');

obj.methodOne();

// The spy and the original function are called.
expect(methodOneCalls).toBe(1);
expect(spy1.mock.calls).toHaveLength(1);

spy1.mockRestore();

obj.methodOne();

// After restoring the spy only the real method bumps its call count, not the spy.
expect(methodOneCalls).toBe(2);
expect(spy1.mock.calls).toHaveLength(0);
});

it('supports restoring all spies', () => {
let methodOneCalls = 0;
let methodTwoCalls = 0;
Expand All @@ -1501,25 +1565,25 @@ describe('moduleMocker', () => {
const spy1 = moduleMocker.spyOn(obj, 'methodOne', 'get');
const spy2 = moduleMocker.spyOn(obj, 'methodTwo', 'get');

// First, we call with the spies: both spies and both original functions
// should be called.
obj.methodOne();
obj.methodTwo();

// Both spies and both original functions got called.
expect(methodOneCalls).toBe(1);
expect(methodTwoCalls).toBe(1);
expect(spy1.mock.calls).toHaveLength(1);
expect(spy2.mock.calls).toHaveLength(1);

moduleMocker.restoreAllMocks();

// Then, after resetting all mocks, we call methods again. Only the real
// methods should bump their count, not the spies.
obj.methodOne();
obj.methodTwo();

// After restoring all mocks only the real methods bump their count, not the spies.
expect(methodOneCalls).toBe(2);
expect(methodTwoCalls).toBe(2);
expect(spy1.mock.calls).toHaveLength(1);
expect(spy2.mock.calls).toHaveLength(1);
expect(spy1.mock.calls).toHaveLength(0);
expect(spy2.mock.calls).toHaveLength(0);
});

it('should work with getters on the prototype chain', () => {
Expand Down Expand Up @@ -1587,6 +1651,34 @@ describe('moduleMocker', () => {
expect(obj.property).toBe(true);
});

it('supports restoring a spy on the prototype chain', () => {
let methodOneCalls = 0;
const prototype = {
get methodOne() {
return function () {
methodOneCalls++;
};
},
};
const obj = Object.create(prototype, {});

const spy1 = moduleMocker.spyOn(obj, 'methodOne', 'get');

obj.methodOne();

// The spy and the original function are called.
expect(methodOneCalls).toBe(1);
expect(spy1.mock.calls).toHaveLength(1);

spy1.mockRestore();

obj.methodOne();

// After restoring the spy only the real method bumps its call count, not the spy.
expect(methodOneCalls).toBe(2);
expect(spy1.mock.calls).toHaveLength(0);
});

it('supports restoring all spies on the prototype chain', () => {
let methodOneCalls = 0;
let methodTwoCalls = 0;
Expand All @@ -1607,25 +1699,25 @@ describe('moduleMocker', () => {
const spy1 = moduleMocker.spyOn(obj, 'methodOne', 'get');
const spy2 = moduleMocker.spyOn(obj, 'methodTwo', 'get');

// First, we call with the spies: both spies and both original functions
// should be called.
obj.methodOne();
obj.methodTwo();

// Both spies and both original functions got called.
expect(methodOneCalls).toBe(1);
expect(methodTwoCalls).toBe(1);
expect(spy1.mock.calls).toHaveLength(1);
expect(spy2.mock.calls).toHaveLength(1);

moduleMocker.restoreAllMocks();

// Then, after resetting all mocks, we call methods again. Only the real
// methods should bump their count, not the spies.
obj.methodOne();
obj.methodTwo();

// After restoring all mocks only the real methods bump their count, not the spies.
expect(methodOneCalls).toBe(2);
expect(methodTwoCalls).toBe(2);
expect(spy1.mock.calls).toHaveLength(1);
expect(spy2.mock.calls).toHaveLength(1);
expect(spy1.mock.calls).toHaveLength(0);
expect(spy2.mock.calls).toHaveLength(0);
});
});

Expand Down Expand Up @@ -1664,7 +1756,7 @@ describe('moduleMocker', () => {
expect(obj.property).toBe(1);
});

it('should allow mocking with value of different value', () => {
it('should allow mocking with value of different type', () => {
const obj = {
property: 1,
};
Expand Down
1 change: 1 addition & 0 deletions packages/jest-mock/src/index.ts
Expand Up @@ -1414,6 +1414,7 @@ export class ModuleMocker {
}

restoreAllMocks(): void {
this._mockState = new WeakMap();
this._spyState.forEach(restore => restore());
this._spyState = new Set();
}
Expand Down

0 comments on commit 66fb417

Please sign in to comment.