From efe65d4d7b265e328f069df57661ba67594d5f9f Mon Sep 17 00:00:00 2001 From: "G. R" <4550801+gricard@users.noreply.github.com> Date: Sun, 8 Oct 2017 16:37:50 -0400 Subject: [PATCH] Added support for naming mocked functions (#4586) * Added ability to specify a name for mocked functions * Added integration tests for mock name functionality * Added unit tests for mock name functionality * Linting fix * Switched from snapshot matching to regex matching because the snapshots vary by platform, apparently * Re-add accidentally removed doc section * Fix the order of replaced doc section * Fix test syntax * Additional unit tests * Fix linting problems * Removed functionality of having mock name passed as argument to jest.fn() and only use jest.fn().mockName() to set it * Linting fix Remove obsolete unit tests * Add a test to confirm that mockReset() clears out the mockName() value. * Updated documentation to note that mockReset() clears out the mockName() value also * Added tests to ensure mockClear() & mockReset() do not affect mockName() value * Changed mockName() API to return the current mock name via a separate getMockName() instead of overloading mockName() function to return it when no arguments are passed * Update MockFunctionAPI.md --- docs/en/MockFunctionAPI.md | 24 ++++++- docs/en/MockFunctions.md | 13 ++++ .../__tests__/mock_names.test.js | 67 +++++++++++++++++++ .../__tests__/index.js | 18 +++++ .../with-empty-mock-name-not-called/index.js | 10 +++ .../package.json | 6 ++ .../with-empty-mock-name/__tests__/index.js | 18 +++++ .../mock-names/with-empty-mock-name/index.js | 10 +++ .../with-empty-mock-name/package.json | 6 ++ .../__tests__/index.js | 18 +++++ .../with-mock-name-call-times-fail/index.js | 10 +++ .../package.json | 6 ++ .../__tests__/index.js | 21 ++++++ .../with-mock-name-call-times-pass/index.js | 10 +++ .../package.json | 6 ++ .../__tests__/index.js | 17 +++++ .../with-mock-name-not-called-fail/index.js | 10 +++ .../package.json | 6 ++ .../__tests__/index.js | 17 +++++ .../with-mock-name-not-called-pass/index.js | 10 +++ .../package.json | 6 ++ .../__tests__/index.js | 17 +++++ .../with-mock-name-not-called/index.js | 10 +++ .../with-mock-name-not-called/package.json | 6 ++ .../with-mock-name/__tests__/index.js | 17 +++++ .../mock-names/with-mock-name/index.js | 10 +++ .../mock-names/with-mock-name/package.json | 6 ++ .../__tests__/index.js | 17 +++++ .../without-mock-name-not-called/index.js | 10 +++ .../without-mock-name-not-called/package.json | 6 ++ .../without-mock-name/__tests__/index.js | 17 +++++ .../mock-names/without-mock-name/index.js | 10 +++ .../mock-names/without-mock-name/package.json | 6 ++ packages/expect/src/spy_matchers.js | 29 ++++---- .../jest-mock/src/__tests__/jest_mock.test.js | 36 ++++++++++ packages/jest-mock/src/index.js | 15 +++++ 36 files changed, 503 insertions(+), 18 deletions(-) create mode 100644 integration_tests/__tests__/mock_names.test.js create mode 100644 integration_tests/mock-names/with-empty-mock-name-not-called/__tests__/index.js create mode 100644 integration_tests/mock-names/with-empty-mock-name-not-called/index.js create mode 100644 integration_tests/mock-names/with-empty-mock-name-not-called/package.json create mode 100644 integration_tests/mock-names/with-empty-mock-name/__tests__/index.js create mode 100644 integration_tests/mock-names/with-empty-mock-name/index.js create mode 100644 integration_tests/mock-names/with-empty-mock-name/package.json create mode 100644 integration_tests/mock-names/with-mock-name-call-times-fail/__tests__/index.js create mode 100644 integration_tests/mock-names/with-mock-name-call-times-fail/index.js create mode 100644 integration_tests/mock-names/with-mock-name-call-times-fail/package.json create mode 100644 integration_tests/mock-names/with-mock-name-call-times-pass/__tests__/index.js create mode 100644 integration_tests/mock-names/with-mock-name-call-times-pass/index.js create mode 100644 integration_tests/mock-names/with-mock-name-call-times-pass/package.json create mode 100644 integration_tests/mock-names/with-mock-name-not-called-fail/__tests__/index.js create mode 100644 integration_tests/mock-names/with-mock-name-not-called-fail/index.js create mode 100644 integration_tests/mock-names/with-mock-name-not-called-fail/package.json create mode 100644 integration_tests/mock-names/with-mock-name-not-called-pass/__tests__/index.js create mode 100644 integration_tests/mock-names/with-mock-name-not-called-pass/index.js create mode 100644 integration_tests/mock-names/with-mock-name-not-called-pass/package.json create mode 100644 integration_tests/mock-names/with-mock-name-not-called/__tests__/index.js create mode 100644 integration_tests/mock-names/with-mock-name-not-called/index.js create mode 100644 integration_tests/mock-names/with-mock-name-not-called/package.json create mode 100644 integration_tests/mock-names/with-mock-name/__tests__/index.js create mode 100644 integration_tests/mock-names/with-mock-name/index.js create mode 100644 integration_tests/mock-names/with-mock-name/package.json create mode 100644 integration_tests/mock-names/without-mock-name-not-called/__tests__/index.js create mode 100644 integration_tests/mock-names/without-mock-name-not-called/index.js create mode 100644 integration_tests/mock-names/without-mock-name-not-called/package.json create mode 100644 integration_tests/mock-names/without-mock-name/__tests__/index.js create mode 100644 integration_tests/mock-names/without-mock-name/index.js create mode 100644 integration_tests/mock-names/without-mock-name/package.json diff --git a/docs/en/MockFunctionAPI.md b/docs/en/MockFunctionAPI.md index b6052928ac3d..29c151831311 100644 --- a/docs/en/MockFunctionAPI.md +++ b/docs/en/MockFunctionAPI.md @@ -18,6 +18,9 @@ Mock functions are also known as "spies", because they let you spy on the behavi ## Reference +### `mockFn.getMockName()` +Returns the mock name string set by calling `mockFn.mockName(value)`. + ### `mockFn.mock.calls` An array that represents all calls that have been made into this mock function. Each call is represented by an array of arguments that were passed during the call. @@ -55,7 +58,7 @@ Beware that `mockClear` will replace `mockFn.mock`, not just [`mockFn.mock.calls The [`clearMocks`](configuration.html#clearmocks-boolean) configuration option is available to clear mocks automatically between tests. ### `mockFn.mockReset()` -Resets all information stored in the mock, including any inital implementation given. +Resets all information stored in the mock, including any initial implementation and mock name given. This is useful when you want to completely restore a mock back to its initial state. @@ -138,6 +141,25 @@ console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); > 'first call', 'second call', 'default', 'default' ``` +### `mockFn.mockName(value)` +Accepts a string to use in test result output in place of "jest.fn()" to indicate which mock function is being referenced. + +For example: + +```js +const mockFn = jest.fn().mockName('mockedFunction'); +// mockFn(); +expect(mockFn).toHaveBeenCalled(); +``` + +Will result in this error: +``` + expect(mockedFunction).toHaveBeenCalled() + + Expected mock function to have been called. +``` + + ### `mockFn.mockReturnThis()` Just a simple sugar function for: diff --git a/docs/en/MockFunctions.md b/docs/en/MockFunctions.md index f0a0c8149477..39db52b77afe 100644 --- a/docs/en/MockFunctions.md +++ b/docs/en/MockFunctions.md @@ -217,6 +217,19 @@ const otherObj = { }; ``` +## Mock Names + +You can optionally provide a name for your mock functions, which will be displayed instead of "jest.fn()" in test error output. Use this if you want to be able to quickly identify the mock function reporting an error in your test output. + +```javascript +const myMockFn = jest.fn() + .mockReturnValue('default') + .mockImplementation(scalar => 42 + scalar) + .mockName('add42'); + +const myMockFn2 = jest.fn(scalar => 42 + scalar, 'add42'); +``` + ## Custom Matchers Finally, in order to make it simpler to assert how mock functions have been diff --git a/integration_tests/__tests__/mock_names.test.js b/integration_tests/__tests__/mock_names.test.js new file mode 100644 index 000000000000..0ba6cf786110 --- /dev/null +++ b/integration_tests/__tests__/mock_names.test.js @@ -0,0 +1,67 @@ +/** + * 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 without mock name, mock called', () => { + const {stderr, status} = runJest('mock-names/without-mock-name'); + + expect(status).toBe(0); + expect(stderr).toMatch(/PASS/); +}); + +test('suite without mock name, mock not called', () => { + const {stderr, status} = runJest('mock-names/without-mock-name-not-called'); + + expect(status).toBe(1); + expect(stderr).toMatch(/expect\(jest\.fn\(\)\)\.toHaveBeenCalled/); +}); + +test('suite with mock name, expect mock not called', () => { + const {stderr, status} = runJest('mock-names/with-mock-name-not-called-pass'); + + expect(status).toBe(0); + expect(stderr).toMatch(/PASS/); +}); + +test('suite with mock name, mock called, expect fail', () => { + const {stderr, status} = runJest('mock-names/with-mock-name-not-called-fail'); + + expect(status).toBe(1); + expect(stderr).toMatch(/expect\(myMockedFunction\)\.not\.toHaveBeenCalled/); +}); + +test('suite with mock name, mock called 5 times', () => { + const {stderr, status} = runJest('mock-names/with-mock-name-call-times-pass'); + + expect(status).toBe(0); + expect(stderr).toMatch(/PASS/); +}); + +test('suite with mock name, mock not called 5 times, expect fail', () => { + const {stderr, status} = runJest('mock-names/with-mock-name-call-times-fail'); + + expect(status).toBe(1); + expect(stderr).toMatch(/expect\(myMockedFunction\)\.toHaveBeenCalledTimes/); +}); + +test('suite with mock name, mock called', () => { + const {stderr, status} = runJest('mock-names/with-mock-name'); + + expect(status).toBe(0); + expect(stderr).toMatch(/PASS/); +}); + +test('suite with mock name, mock not called', () => { + const {stderr, status} = runJest('mock-names/with-mock-name-not-called'); + + expect(status).toBe(1); + expect(stderr).toMatch(/expect\(myMockedFunction\)\.toHaveBeenCalled/); +}); diff --git a/integration_tests/mock-names/with-empty-mock-name-not-called/__tests__/index.js b/integration_tests/mock-names/with-empty-mock-name-not-called/__tests__/index.js new file mode 100644 index 000000000000..5cd94b1c2440 --- /dev/null +++ b/integration_tests/mock-names/with-empty-mock-name-not-called/__tests__/index.js @@ -0,0 +1,18 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +// empty mock name should result in default 'jest.fn()' output +const mockFn = jest.fn(importedFn).mockName(''); + +test('first test', () => { + // mockFn explicitly not called to test error output + expect(mockFn).toHaveBeenCalledTimes(1); +}); diff --git a/integration_tests/mock-names/with-empty-mock-name-not-called/index.js b/integration_tests/mock-names/with-empty-mock-name-not-called/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-empty-mock-name-not-called/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-empty-mock-name-not-called/package.json b/integration_tests/mock-names/with-empty-mock-name-not-called/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-empty-mock-name-not-called/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-empty-mock-name/__tests__/index.js b/integration_tests/mock-names/with-empty-mock-name/__tests__/index.js new file mode 100644 index 000000000000..5f49fc4afef6 --- /dev/null +++ b/integration_tests/mock-names/with-empty-mock-name/__tests__/index.js @@ -0,0 +1,18 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +// empty mock name should result in default 'jest.fn()' output +const mockFn = jest.fn(importedFn).mockName(''); + +test('first test', () => { + mockFn(); + expect(mockFn).toHaveBeenCalledTimes(1); +}); diff --git a/integration_tests/mock-names/with-empty-mock-name/index.js b/integration_tests/mock-names/with-empty-mock-name/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-empty-mock-name/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-empty-mock-name/package.json b/integration_tests/mock-names/with-empty-mock-name/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-empty-mock-name/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-mock-name-call-times-fail/__tests__/index.js b/integration_tests/mock-names/with-mock-name-call-times-fail/__tests__/index.js new file mode 100644 index 000000000000..6b0ca540f6d6 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-call-times-fail/__tests__/index.js @@ -0,0 +1,18 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn).mockName('myMockedFunction'); + +test('first test', () => { + mockFn(); + mockFn(); + expect(mockFn).toHaveBeenCalledTimes(5); +}); diff --git a/integration_tests/mock-names/with-mock-name-call-times-fail/index.js b/integration_tests/mock-names/with-mock-name-call-times-fail/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-call-times-fail/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-mock-name-call-times-fail/package.json b/integration_tests/mock-names/with-mock-name-call-times-fail/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-call-times-fail/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-mock-name-call-times-pass/__tests__/index.js b/integration_tests/mock-names/with-mock-name-call-times-pass/__tests__/index.js new file mode 100644 index 000000000000..1d4df77fdddc --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-call-times-pass/__tests__/index.js @@ -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. + */ + +'use strict'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn).mockName('myMockedFunction'); + +test('first test', () => { + mockFn(); + mockFn(); + mockFn(); + mockFn(); + mockFn(); + expect(mockFn).toHaveBeenCalledTimes(5); +}); diff --git a/integration_tests/mock-names/with-mock-name-call-times-pass/index.js b/integration_tests/mock-names/with-mock-name-call-times-pass/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-call-times-pass/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-mock-name-call-times-pass/package.json b/integration_tests/mock-names/with-mock-name-call-times-pass/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-call-times-pass/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-mock-name-not-called-fail/__tests__/index.js b/integration_tests/mock-names/with-mock-name-not-called-fail/__tests__/index.js new file mode 100644 index 000000000000..01aada892249 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called-fail/__tests__/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn).mockName('myMockedFunction'); + +test('first test', () => { + mockFn(); + expect(mockFn).not.toHaveBeenCalled(); +}); diff --git a/integration_tests/mock-names/with-mock-name-not-called-fail/index.js b/integration_tests/mock-names/with-mock-name-not-called-fail/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called-fail/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-mock-name-not-called-fail/package.json b/integration_tests/mock-names/with-mock-name-not-called-fail/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called-fail/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-mock-name-not-called-pass/__tests__/index.js b/integration_tests/mock-names/with-mock-name-not-called-pass/__tests__/index.js new file mode 100644 index 000000000000..abfe2fcfe604 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called-pass/__tests__/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn).mockName('myMockedFunction'); + +test('first test', () => { + // mockFn explicitly not called to test error output + expect(mockFn).not.toHaveBeenCalled(); +}); diff --git a/integration_tests/mock-names/with-mock-name-not-called-pass/index.js b/integration_tests/mock-names/with-mock-name-not-called-pass/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called-pass/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-mock-name-not-called-pass/package.json b/integration_tests/mock-names/with-mock-name-not-called-pass/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called-pass/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-mock-name-not-called/__tests__/index.js b/integration_tests/mock-names/with-mock-name-not-called/__tests__/index.js new file mode 100644 index 000000000000..57ed74faed86 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called/__tests__/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn).mockName('myMockedFunction'); + +test('first test', () => { + // mockFn explicitly not called to test error output + expect(mockFn).toHaveBeenCalledTimes(1); +}); diff --git a/integration_tests/mock-names/with-mock-name-not-called/index.js b/integration_tests/mock-names/with-mock-name-not-called/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-mock-name-not-called/package.json b/integration_tests/mock-names/with-mock-name-not-called/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name-not-called/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/with-mock-name/__tests__/index.js b/integration_tests/mock-names/with-mock-name/__tests__/index.js new file mode 100644 index 000000000000..b4970abf018e --- /dev/null +++ b/integration_tests/mock-names/with-mock-name/__tests__/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn).mockName('myMockedFunction'); + +test('first test', () => { + mockFn(); + expect(mockFn).toHaveBeenCalledTimes(1); +}); diff --git a/integration_tests/mock-names/with-mock-name/index.js b/integration_tests/mock-names/with-mock-name/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/with-mock-name/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/with-mock-name/package.json b/integration_tests/mock-names/with-mock-name/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/with-mock-name/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/without-mock-name-not-called/__tests__/index.js b/integration_tests/mock-names/without-mock-name-not-called/__tests__/index.js new file mode 100644 index 000000000000..f85711e32fd9 --- /dev/null +++ b/integration_tests/mock-names/without-mock-name-not-called/__tests__/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn); + +test('first test', () => { + // mockFn explicitly not called to test error output + expect(mockFn).toHaveBeenCalledTimes(1); +}); diff --git a/integration_tests/mock-names/without-mock-name-not-called/index.js b/integration_tests/mock-names/without-mock-name-not-called/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/without-mock-name-not-called/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/without-mock-name-not-called/package.json b/integration_tests/mock-names/without-mock-name-not-called/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/without-mock-name-not-called/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/integration_tests/mock-names/without-mock-name/__tests__/index.js b/integration_tests/mock-names/without-mock-name/__tests__/index.js new file mode 100644 index 000000000000..5ffe16523bd1 --- /dev/null +++ b/integration_tests/mock-names/without-mock-name/__tests__/index.js @@ -0,0 +1,17 @@ +/** + * 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'; + +jest.mock('../'); +const importedFn = require('../'); +const mockFn = jest.fn(importedFn); + +test('first test', () => { + mockFn(); + expect(mockFn).toHaveBeenCalledTimes(1); +}); diff --git a/integration_tests/mock-names/without-mock-name/index.js b/integration_tests/mock-names/without-mock-name/index.js new file mode 100644 index 000000000000..5233c06878bf --- /dev/null +++ b/integration_tests/mock-names/without-mock-name/index.js @@ -0,0 +1,10 @@ +/** + * 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 + */ + +module.exports = () => {}; diff --git a/integration_tests/mock-names/without-mock-name/package.json b/integration_tests/mock-names/without-mock-name/package.json new file mode 100644 index 000000000000..b54d0a3264e7 --- /dev/null +++ b/integration_tests/mock-names/without-mock-name/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node", + "clearMocks": true + } +} diff --git a/packages/expect/src/spy_matchers.js b/packages/expect/src/spy_matchers.js index 17a681925906..8310e0baad7d 100644 --- a/packages/expect/src/spy_matchers.js +++ b/packages/expect/src/spy_matchers.js @@ -25,17 +25,13 @@ import { import {equals} from './jasmine_utils'; import {iterableEquality, partition} from './utils'; -const RECEIVED_NAME = { - 'mock function': 'jest.fn()', - spy: 'spy', -}; - const createToBeCalledMatcher = matcherName => (received, expected) => { ensureNoExpected(expected, matcherName); ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); const type = receivedIsSpy ? 'spy' : 'mock function'; + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); const count = receivedIsSpy ? received.calls.count() : received.mock.calls.length; @@ -45,12 +41,12 @@ const createToBeCalledMatcher = matcherName => (received, expected) => { const pass = count > 0; const message = pass ? () => - matcherHint('.not' + matcherName, RECEIVED_NAME[type], '') + + matcherHint('.not' + matcherName, receivedName, '') + '\n\n' + `Expected ${type} not to be called ` + formatReceivedCalls(calls, CALL_PRINT_LIMIT, {sameSentence: true}) : () => - matcherHint(matcherName, RECEIVED_NAME[type], '') + + matcherHint(matcherName, receivedName, '') + '\n\n' + `Expected ${type} to have been called.`; @@ -65,6 +61,7 @@ const createToBeCalledWithMatcher = matcherName => ( const receivedIsSpy = isSpy(received); const type = receivedIsSpy ? 'spy' : 'mock function'; + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); const calls = receivedIsSpy ? received.calls.all().map(x => x.args) : received.mock.calls; @@ -76,12 +73,12 @@ const createToBeCalledWithMatcher = matcherName => ( const message = pass ? () => - matcherHint('.not' + matcherName, RECEIVED_NAME[type]) + + matcherHint('.not' + matcherName, receivedName) + '\n\n' + `Expected ${type} not to have been called with:\n` + ` ${printExpected(expected)}` : () => - matcherHint(matcherName, RECEIVED_NAME[type]) + + matcherHint(matcherName, receivedName) + '\n\n' + `Expected ${type} to have been called with:\n` + formatMismatchedCalls(fail, expected, CALL_PRINT_LIMIT); @@ -97,6 +94,7 @@ const createLastCalledWithMatcher = matcherName => ( const receivedIsSpy = isSpy(received); const type = receivedIsSpy ? 'spy' : 'mock function'; + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); const calls = receivedIsSpy ? received.calls.all().map(x => x.args) : received.mock.calls; @@ -104,12 +102,12 @@ const createLastCalledWithMatcher = matcherName => ( const message = pass ? () => - matcherHint('.not' + matcherName, RECEIVED_NAME[type]) + + matcherHint('.not' + matcherName, receivedName) + '\n\n' + `Expected ${type} to not have been last called with:\n` + ` ${printExpected(expected)}` : () => - matcherHint(matcherName, RECEIVED_NAME[type]) + + matcherHint(matcherName, receivedName) + '\n\n' + `Expected ${type} to have been last called with:\n` + formatMismatchedCalls(calls, expected, LAST_CALL_PRINT_LIMIT); @@ -129,23 +127,20 @@ const spyMatchers: MatchersObject = { const receivedIsSpy = isSpy(received); const type = receivedIsSpy ? 'spy' : 'mock function'; + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); const count = receivedIsSpy ? received.calls.count() : received.mock.calls.length; const pass = count === expected; const message = pass ? () => - matcherHint( - '.not' + matcherName, - RECEIVED_NAME[type], - String(expected), - ) + + matcherHint('.not' + matcherName, receivedName, String(expected)) + `\n\n` + `Expected ${type} not to be called ` + `${EXPECTED_COLOR(pluralize('time', expected))}, but it was` + ` called exactly ${RECEIVED_COLOR(pluralize('time', count))}.` : () => - matcherHint(matcherName, RECEIVED_NAME[type], String(expected)) + + matcherHint(matcherName, receivedName, String(expected)) + '\n\n' + `Expected ${type} to have been called ` + `${EXPECTED_COLOR(pluralize('time', expected))},` + diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index 68f7c3838c19..56106ca9d846 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -476,6 +476,42 @@ describe('moduleMocker', () => { expect(moduleMocker.isMockFunction(mockFn)).toBe(true); }); + test('default mockName is jest.fn()', () => { + const fn = jest.fn(); + expect(fn.getMockName()).toBe('jest.fn()'); + }); + + test('mockName sets the mock name', () => { + const fn = jest.fn(); + fn.mockName('myMockFn'); + expect(fn.getMockName()).toBe('myMockFn'); + }); + + test('mockName gets reset by mockReset', () => { + const fn = jest.fn(); + expect(fn.getMockName()).toBe('jest.fn()'); + fn.mockName('myMockFn'); + expect(fn.getMockName()).toBe('myMockFn'); + fn.mockReset(); + expect(fn.getMockName()).toBe('jest.fn()'); + }); + + test('mockName is not reset by mockRestore', () => { + const fn = jest.fn(() => false); + fn.mockName('myMockFn'); + expect(fn.getMockName()).toBe('myMockFn'); + fn.mockRestore(); + expect(fn.getMockName()).toBe('myMockFn'); + }); + + test('mockName is not reset by mockClear', () => { + const fn = jest.fn(() => false); + fn.mockName('myMockFn'); + expect(fn.getMockName()).toBe('myMockFn'); + fn.mockClear(); + expect(fn.getMockName()).toBe('myMockFn'); + }); + describe('spyOn', () => { it('should work', () => { let isOriginalCalled = false; diff --git a/packages/jest-mock/src/index.js b/packages/jest-mock/src/index.js index f40748d041ea..13468989bc5b 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.js @@ -30,6 +30,7 @@ type MockFunctionConfig = { isReturnValueLastSet: boolean, defaultReturnValue: any, mockImpl: any, + mockName: string, specificReturnValues: Array, specificMockImpls: Array, }; @@ -269,6 +270,7 @@ class ModuleMockerClass { defaultReturnValue: undefined, isReturnValueLastSet: false, mockImpl: undefined, + mockName: 'jest.fn()', specificMockImpls: [], specificReturnValues: [], }; @@ -434,6 +436,19 @@ class ModuleMockerClass { return this; }); + f.mockName = name => { + if (name) { + const mockConfig = this._ensureMockConfig(f); + mockConfig.mockName = name; + } + return f; + }; + + f.getMockName = () => { + const mockConfig = this._ensureMockConfig(f); + return mockConfig.mockName || 'jest.fn()'; + }; + if (metadata.mockImpl) { f.mockImplementation(metadata.mockImpl); }