Skip to content

Commit

Permalink
feat: add createMockFromModule to replace genMockFromModule (#9962)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed May 3, 2020
1 parent 8147af1 commit c665f22
Show file tree
Hide file tree
Showing 26 changed files with 76 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- `[jest-environment-jsdom]` [**BREAKING**] Upgrade `jsdom` to v16 ([#9606](https://github.com/facebook/jest/pull/9606))
- `[@jest/fake-timers]` Add possibility to use a modern implementation of fake timers, backed by `@sinonjs/fake-timers` ([#7776](https://github.com/facebook/jest/pull/7776))
- `[jest-runtime]` Add `createMockFromModule` as an alias for `genMockFromModule` ([#9962](https://github.com/facebook/jest/pull/9962))

### Fixes

Expand Down
16 changes: 10 additions & 6 deletions docs/JestObjectAPI.md
Expand Up @@ -90,7 +90,11 @@ test('original implementation', () => {

_Note: this method was previously called `autoMockOn`. When using `babel-jest`, calls to `enableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOn` if you want to explicitly avoid this behavior._

### `jest.genMockFromModule(moduleName)`
### `jest.createMockFromModule(moduleName)`

##### renamed in Jest **26.0.0+**

Also under the alias: `.genMockFromModule(moduleName)`

Given the name of a module, use the automatic mocking system to generate a mocked version of the module for you.

Expand All @@ -109,17 +113,17 @@ export default {
```

```js
// __tests__/genMockFromModule.test.js
const utils = jest.genMockFromModule('../utils').default;
// __tests__/createMockFromModule.test.js
const utils = jest.createMockFromModule('../utils').default;
utils.isAuthorized = jest.fn(secret => secret === 'not wizard');

test('implementation created by jest.genMockFromModule', () => {
test('implementation created by jest.createMockFromModule', () => {
expect(utils.authorize.mock).toBeTruthy();
expect(utils.isAuthorized('not wizard')).toEqual(true);
});
```

This is how `genMockFromModule` will mock the following data types:
This is how `createMockFromModule` will mock the following data types:

#### `Function`

Expand Down Expand Up @@ -176,7 +180,7 @@ module.exports = {

```js
// __tests__/example.test.js
const example = jest.genMockFromModule('./example');
const example = jest.createMockFromModule('./example');

test('should run example code', () => {
// creates a new mocked function with no formal arguments.
Expand Down
4 changes: 2 additions & 2 deletions docs/ManualMocks.md
Expand Up @@ -64,7 +64,7 @@ Since we'd like our tests to avoid actually hitting the disk (that's pretty slow

const path = require('path');

const fs = jest.genMockFromModule('fs');
const fs = jest.createMockFromModule('fs');

// This is a custom function that our tests can use during setup to specify
// what the files on the "mock" filesystem should look like when any of the
Expand Down Expand Up @@ -124,7 +124,7 @@ describe('listFilesInDirectorySync', () => {
});
```

The example mock shown here uses [`jest.genMockFromModule`](JestObjectAPI.md#jestgenmockfrommodulemodulename) to generate an automatic mock, and overrides its default behavior. This is the recommended approach, but is completely optional. If you do not want to use the automatic mock at all, you can export your own functions from the mock file. One downside to fully manual mocks is that they're manual – meaning you have to manually update them any time the module they are mocking changes. Because of this, it's best to use or extend the automatic mock when it works for your needs.
The example mock shown here uses [`jest.createMockFromModule`](JestObjectAPI.md#jestcreatemockfrommodulemodulename) to generate an automatic mock, and overrides its default behavior. This is the recommended approach, but is completely optional. If you do not want to use the automatic mock at all, you can export your own functions from the mock file. One downside to fully manual mocks is that they're manual – meaning you have to manually update them any time the module they are mocking changes. Because of this, it's best to use or extend the automatic mock when it works for your needs.

To ensure that a manual mock and its real implementation stay in sync, it might be useful to require the real module using [`jest.requireActual(moduleName)`](JestObjectAPI.md#jestrequireactualmodulename) in your manual mock and amending it with mock functions before exporting it.

Expand Down
2 changes: 1 addition & 1 deletion e2e/runtime-internal-module-registry/__mocks__/fs.js
Expand Up @@ -7,7 +7,7 @@

'use strict';

const fs = jest.genMockFromModule('fs');
const fs = jest.createMockFromModule('fs');

let mkdirWasCalled = false;

Expand Down
Expand Up @@ -7,8 +7,8 @@ test('implementation created by automock', () => {
expect(utils.isAuthorized()).toBeUndefined();
});

test('implementation created by jest.genMockFromModule', () => {
const utils = jest.genMockFromModule('../utils').default;
test('implementation created by jest.createMockFromModule', () => {
const utils = jest.createMockFromModule('../utils').default;
utils.isAuthorized = jest.fn(secret => secret === 'not wizard');

expect(utils.authorize.mock).toBeTruthy();
Expand Down
2 changes: 1 addition & 1 deletion examples/manual-mocks/__mocks__/fs.js
Expand Up @@ -4,7 +4,7 @@

const path = require('path');

const fs = jest.genMockFromModule('fs');
const fs = jest.createMockFromModule('fs');

// This is a custom function that our tests can use during setup to specify
// what the files on the "mock" filesystem should look like when any of the
Expand Down
2 changes: 1 addition & 1 deletion examples/manual-mocks/__mocks__/lodash.js
@@ -1,6 +1,6 @@
// Copyright 2004-present Facebook. All Rights Reserved.

const lodash = jest.genMockFromModule('lodash');
const lodash = jest.createMockFromModule('lodash');

lodash.head = arr => 5;

Expand Down
2 changes: 1 addition & 1 deletion examples/manual-mocks/models/__mocks__/user.js
@@ -1,6 +1,6 @@
// Copyright 2004-present Facebook. All Rights Reserved.

const user = jest.genMockFromModule('../user');
const user = jest.createMockFromModule('../user');

user.getAuthenticated = () => ({
age: 622,
Expand Down
2 changes: 1 addition & 1 deletion examples/module-mock/__tests__/partial_mock.js
Expand Up @@ -9,7 +9,7 @@ import defaultExport, {apple, strawberry} from '../fruit';

jest.mock('../fruit', () => {
const originalModule = jest.requireActual('../fruit');
const mockedModule = jest.genMockFromModule('../fruit');
const mockedModule = jest.createMockFromModule('../fruit');

//Mock the default export and named export 'apple'.
return {
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-config/src/__mocks__/fs.js
Expand Up @@ -7,7 +7,7 @@

'use strict';

const fs = jest.genMockFromModule('fs');
const fs = jest.createMockFromModule('fs');

const mockFiles = new Map();
function __setMockFiles(newMockFiles) {
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-config/src/__mocks__/os.js
Expand Up @@ -7,7 +7,7 @@

'use strict';

const os = jest.genMockFromModule('os');
const os = jest.createMockFromModule('os');

let cpus;
function __setCpus(newCpus) {
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-environment-jsdom/src/__mocks__/index.ts
Expand Up @@ -8,7 +8,7 @@

const vm = jest.requireActual('vm');

const JSDOMEnvironment = jest.genMockFromModule('../index') as jest.Mock;
const JSDOMEnvironment = jest.createMockFromModule('../index') as jest.Mock;

JSDOMEnvironment.mockImplementation(function (config) {
// @ts-ignore
Expand Down
10 changes: 10 additions & 0 deletions packages/jest-environment/src/index.ts
Expand Up @@ -126,8 +126,18 @@ export interface Jest {
*
* This is useful when you want to create a manual mock that extends the
* automatic mock's behavior.
*
* @deprecated Use `jest.createMockFromModule()` instead
*/
genMockFromModule(moduleName: string): unknown;
/**
* Given the name of a module, use the automatic mocking system to generate a
* mocked version of the module for you.
*
* This is useful when you want to create a manual mock that extends the
* automatic mock's behavior.
*/
createMockFromModule(moduleName: string): unknown;
/**
* Determines if the given function is a mocked function.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-haste-map/src/__tests__/worker.test.js
Expand Up @@ -38,7 +38,7 @@ jest.mock('graceful-fs', () => {
};

return {
...jest.genMockFromModule('graceful-fs'),
...jest.createMockFromModule('graceful-fs'),
readFileSync: jest.fn((path, options) => {
if (mockFs[path]) {
return options === 'utf8' ? mockFs[path] : Buffer.from(mockFs[path]);
Expand Down
Expand Up @@ -243,4 +243,24 @@ describe('dependencyExtractor', () => {
`;
expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4']));
});

it('should extract dependencies from `jest.createMockFromModule` calls', () => {
const code = `
// Good
jest.createMockFromModule('dep1');
const dep2 = jest.createMockFromModule(
"dep2",
);
if (jest.createMockFromModule(\`dep3\`).cond) {}
jest
.requireMock('dep4');
// Bad
${COMMENT_NO_NEG_LB} foo . jest.createMockFromModule('inv1')
xjest.createMockFromModule('inv2');
jest.createMockFromModulex('inv3');
jest.createMockFromModule('inv4', 'inv5');
`;
expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4']));
});
});
2 changes: 1 addition & 1 deletion packages/jest-haste-map/src/lib/dependencyExtractor.ts
Expand Up @@ -63,7 +63,7 @@ const IMPORT_OR_EXPORT_RE = createRegExp(
const JEST_EXTENSIONS_RE = createRegExp(
[
...functionCallStart(
'jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule)',
'jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule|createMockFromModule)',
),
CAPTURE_STRING_LITERAL(1),
WHITESPACE,
Expand Down
10 changes: 8 additions & 2 deletions packages/jest-regex-util/src/__tests__/index.test.ts
Expand Up @@ -10,7 +10,10 @@ describe('replacePathSepForRegex()', () => {

describe('posix', () => {
beforeAll(() => {
jest.mock('path', () => ({...jest.genMockFromModule('path'), sep: '/'}));
jest.mock('path', () => ({
...jest.createMockFromModule('path'),
sep: '/',
}));
jest.isolateModules(() => {
replacePathSepForRegex = require('../').replacePathSepForRegex;
});
Expand All @@ -24,7 +27,10 @@ describe('replacePathSepForRegex()', () => {

describe('win32', () => {
beforeAll(() => {
jest.mock('path', () => ({...jest.genMockFromModule('path'), sep: '\\'}));
jest.mock('path', () => ({
...jest.createMockFromModule('path'),
sep: '\\',
}));
jest.isolateModules(() => {
replacePathSepForRegex = require('../').replacePathSepForRegex;
});
Expand Down
Expand Up @@ -13,7 +13,7 @@ jest
summarizers: {pkg: jest.fn(() => ({visit: jest.fn()}))},
}))
.mock('istanbul-reports', () => ({
...jest.genMockFromModule('istanbul-reports'),
...jest.createMockFromModule('istanbul-reports'),
create: jest.fn(() => ({execute: jest.fn()})),
}));

Expand Down
Expand Up @@ -19,7 +19,7 @@ describe('Runtime', () => {
createRuntime = require('createRuntime');
});

describe('genMockFromModule', () => {
describe('createMockFromModule', () => {
it('does not cause side effects in the rest of the module system when generating a mock', () =>
createRuntime(__filename).then(runtime => {
const testRequire = runtime.requireModule.bind(
Expand All @@ -33,7 +33,7 @@ describe('Runtime', () => {
expect(origModuleStateValue).toBe('default');

// Generate a mock for a module with side effects
const mock = module.jest.genMockFromModule('ModuleWithSideEffects');
const mock = module.jest.createMockFromModule('ModuleWithSideEffects');

// Make sure we get a mock.
expect(mock.fn()).toBe(undefined);
Expand All @@ -43,8 +43,8 @@ describe('Runtime', () => {
it('resolves mapped modules correctly', () =>
createRuntime(__filename, {moduleNameMapper}).then(runtime => {
const root = runtime.requireModule(runtime.__mockRootPath);
const mockModule = root.jest.genMockFromModule(
'module/name/genMockFromModule',
const mockModule = root.jest.createMockFromModule(
'module/name/createMockFromModule',
);

expect(mockModule.test.mock).toBeTruthy();
Expand All @@ -59,7 +59,7 @@ describe('Runtime', () => {
);

const module = testRequire('RegularModule');
const mockModule = module.jest.genMockFromModule('RegularModule');
const mockModule = module.jest.createMockFromModule('RegularModule');
const testObjectPrototype = Object.getPrototypeOf(module.object);
const mockObjectPrototype = Object.getPrototypeOf(mockModule.object);
expect(mockObjectPrototype).toBe(testObjectPrototype);
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -1483,6 +1483,8 @@ class Runtime {
autoMockOn: enableAutomock,
clearAllMocks,
clearAllTimers: () => _getFakeTimers().clearAllTimers(),
createMockFromModule: (moduleName: string) =>
this._generateMock(from, moduleName),
deepUnmock,
disableAutomock,
doMock: mock,
Expand Down
Expand Up @@ -6,7 +6,7 @@
*/

jest.mock('graceful-fs', () => ({
...jest.genMockFromModule<typeof import('fs')>('fs'),
...jest.createMockFromModule<typeof import('fs')>('fs'),
existsSync: jest.fn().mockReturnValue(true),
readdirSync: jest.fn().mockReturnValue([]),
statSync: jest.fn(filePath => ({
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-snapshot/src/__tests__/utils.test.ts
Expand Up @@ -6,7 +6,7 @@
*/

jest.mock('graceful-fs', () => ({
...jest.genMockFromModule<typeof import('fs')>('fs'),
...jest.createMockFromModule<typeof import('fs')>('fs'),
existsSync: jest.fn().mockReturnValue(true),
}));

Expand Down
2 changes: 1 addition & 1 deletion packages/jest-source-map/src/__tests__/getCallsite.test.ts
Expand Up @@ -11,7 +11,7 @@ import getCallsite from '../getCallsite';

// Node 10.5.x compatibility
jest.mock('graceful-fs', () => ({
...jest.genMockFromModule<typeof import('fs')>('fs'),
...jest.createMockFromModule<typeof import('fs')>('fs'),
ReadStream: jest.requireActual('fs').ReadStream,
WriteStream: jest.requireActual('fs').WriteStream,
}));
Expand Down
Expand Up @@ -10,7 +10,7 @@ import * as fs from 'graceful-fs';
import TestSequencer from '../index';

jest.mock('graceful-fs', () => ({
...jest.genMockFromModule('fs'),
...jest.createMockFromModule('fs'),
existsSync: jest.fn(() => true),
readFileSync: jest.fn(() => '{}'),
}));
Expand Down
Expand Up @@ -13,7 +13,7 @@ jest
.mock('graceful-fs', () =>
// Node 10.5.x compatibility
({
...jest.genMockFromModule('fs'),
...jest.createMockFromModule('fs'),
ReadStream: jest.requireActual('fs').ReadStream,
WriteStream: jest.requireActual('fs').WriteStream,
readFileSync: jest.fn((path, options) => {
Expand Down

0 comments on commit c665f22

Please sign in to comment.