Skip to content

Commit

Permalink
Add restoreMocks config to fix #3580 (#5327)
Browse files Browse the repository at this point in the history
* Added restoreMocks config option

* Changed tests to use jest.spyOn, since that's what restore impacts

* Changed tests to use correct toHaveBeenCalled() syntax

* Added changelog entry
  • Loading branch information
gricard authored and cpojer committed Jan 16, 2018
1 parent 7743c09 commit bb21f0b
Show file tree
Hide file tree
Showing 22 changed files with 161 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

* `[jest-cli]` Fix `EISDIR` when a directory is passed as an argument to `jest`.
([#5317](https://github.com/facebook/jest/pull/5317))
* `[jest-config]` Added restoreMocks config option.
([#5327](https://github.com/facebook/jest/pull/5327))

## jest 22.1.0

Expand Down
9 changes: 9 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,15 @@ argument:
The function should either return a path to the module that should be resolved
or throw an error if the module can't be found.

### `restoreMocks` [boolean]

Default: `false`

Automatically restore mock state between every test. Equivalent to calling
`jest.restoreAllMocks()` between each test. This will lead to any mocks having
their fake implementations removed and restores their initial
implementation.

### `rootDir` [string]

Default: The root of the directory containing your jest's [config file](#) _or_
Expand Down
3 changes: 3 additions & 0 deletions docs/MockFunctionAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ Beware that `mockFn.mockRestore` only works when mock was created with
`jest.spyOn`. Thus you have to take care of restoration yourself when manually
assigning `jest.fn()`.

The [`restoreMocks`](configuration.html#restoremocks-boolean) configuration
option is available to restore mocks automatically between tests.

### `mockFn.mockImplementation(fn)`

Accepts a function that should be used as the implementation of the mock. The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
\\"name\\": \\"[md5 hash]\\",
\\"resetMocks\\": false,
\\"resetModules\\": false,
\\"restoreMocks\\": false,
\\"rootDir\\": \\"<<REPLACED_ROOT_DIR>>\\",
\\"roots\\": [
\\"<<REPLACED_ROOT_DIR>>\\"
Expand Down
21 changes: 21 additions & 0 deletions integration-tests/__tests__/auto_restore_mocks.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
'use strict';

const runJest = require('../runJest');

test('suite with auto-restore', () => {
const result = runJest('auto-restore-mocks/with-auto-restore');
expect(result.status).toBe(0);
});

test('suite without auto-restore', () => {
const result = runJest('auto-restore-mocks/without-auto-restore');
expect(result.status).toBe(0);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

const TestClass = require('../');
const localClass = new TestClass();

test('first test', () => {
jest.spyOn(localClass, 'test').mockImplementation(() => 'ABCD');
expect(localClass.test()).toEqual('ABCD');
expect(localClass.test).toHaveBeenCalledTimes(1);
});

test('second test', () => {
expect(localClass.test()).toEqual('12345');
expect(localClass.test.mock).toBe(undefined);
});
12 changes: 12 additions & 0 deletions integration-tests/auto-restore-mocks/with-auto-restore/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

module.exports = class Test {
test() {
return '12345';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"jest": {
"testEnvironment": "node",
"restoreMocks": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

const TestClass = require('../');
const localClass = new TestClass();

describe('without an explicit restore', () => {
jest.spyOn(localClass, 'test').mockImplementation(() => 'ABCD');

test('first test', () => {
expect(localClass.test()).toEqual('ABCD');
expect(localClass.test).toHaveBeenCalledTimes(1);
});

test('second test', () => {
expect(localClass.test()).toEqual('ABCD');
expect(localClass.test).toHaveBeenCalledTimes(2);
});
});

describe('with an explicit restore', () => {
beforeEach(() => {
jest.restoreAllMocks();
});

test('first test', () => {
jest.spyOn(localClass, 'test').mockImplementation(() => 'ABCD');
expect(localClass.test()).toEqual('ABCD');
expect(localClass.test).toHaveBeenCalledTimes(1);
});

test('second test', () => {
expect(localClass.test()).toEqual('12345');
expect(localClass.test.mock).toBe(undefined);
});
});
12 changes: 12 additions & 0 deletions integration-tests/auto-restore-mocks/without-auto-restore/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

module.exports = class Test {
test() {
return '12345';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"jest": {
"testEnvironment": "node"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ const jestAdapter = async (
runtime.resetAllMocks();
}

if (config.restoreMocks) {
runtime.restoreAllMocks();
}

if (config.timers === 'fake') {
environment.fakeTimers.useFakeTimers();
}
Expand Down
7 changes: 7 additions & 0 deletions packages/jest-cli/src/cli/args.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,13 @@ export const options = {
description: 'A JSON string which allows the use of a custom resolver.',
type: 'string',
},
restoreMocks: {
default: undefined,
description:
'Automatically restore mock state and implementation between every test. ' +
'Equivalent to calling jest.restoreAllMocks() between each test.',
type: 'boolean',
},
rootDir: {
description:
'The root directory that Jest should scan for tests and ' +
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default ({
preset: null,
resetMocks: false,
resetModules: false,
restoreMocks: false,
runTestsByPath: false,
runner: 'jest-runner',
snapshotSerializers: [],
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ const getConfigs = (
resetMocks: options.resetMocks,
resetModules: options.resetModules,
resolver: options.resolver,
restoreMocks: options.restoreMocks,
rootDir: options.rootDir,
roots: options.roots,
runner: options.runner,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ export default function normalize(options: InitialOptions, argv: Argv) {
case 'reporters':
case 'resetMocks':
case 'resetModules':
case 'restoreMocks':
case 'rootDir':
case 'runTestsByPath':
case 'silent':
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/valid_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default ({
resetMocks: false,
resetModules: false,
resolver: '<rootDir>/resolver.js',
restoreMocks: false,
rootDir: '/',
roots: ['<rootDir>'],
runTestsByPath: false,
Expand Down
4 changes: 4 additions & 0 deletions packages/jest-jasmine2/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ async function jasmine2(
environment.fakeTimers.useFakeTimers();
}
}

if (config.restoreMocks) {
runtime.restoreAllMocks();
}
});

env.addReporter(reporter);
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-validate/src/__tests__/fixtures/jest_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const defaultConfig = {
preset: null,
resetMocks: false,
resetModules: false,
restoreMocks: false,
roots: ['<rootDir>'],
snapshotSerializers: [],
testEnvironment: 'jest-environment-jsdom',
Expand Down Expand Up @@ -98,6 +99,7 @@ const validConfig = {
preset: 'react-native',
resetMocks: false,
resetModules: false,
restoreMocks: false,
rootDir: '/',
roots: ['<rootDir>'],
setupFiles: ['<rootDir>/setup.js'],
Expand Down
1 change: 1 addition & 0 deletions test_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = {
resetMocks: false,
resetModules: false,
resolver: null,
restoreMocks: false,
rootDir: '/test_root_dir/',
roots: [],
runner: 'jest-runner',
Expand Down
1 change: 1 addition & 0 deletions types/Argv.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export type Argv = {|
resetMocks: boolean,
resetModules: boolean,
resolver: ?string,
restoreMocks: boolean,
rootDir: string,
roots: Array<string>,
setupFiles: Array<string>,
Expand Down
3 changes: 3 additions & 0 deletions types/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export type DefaultOptions = {|
preset: ?string,
resetMocks: boolean,
resetModules: boolean,
restoreMocks: boolean,
runner: string,
runTestsByPath: boolean,
snapshotSerializers: Array<Path>,
Expand Down Expand Up @@ -117,6 +118,7 @@ export type InitialOptions = {
resetMocks?: boolean,
resetModules?: boolean,
resolver?: ?Path,
restoreMocks?: boolean,
rootDir: Path,
roots?: Array<Path>,
runner?: string,
Expand Down Expand Up @@ -227,6 +229,7 @@ export type ProjectConfig = {|
resetMocks: boolean,
resetModules: boolean,
resolver: ?Path,
restoreMocks: boolean,
rootDir: Path,
roots: Array<Path>,
runner: string,
Expand Down

0 comments on commit bb21f0b

Please sign in to comment.