From 264ef4eee0cf11686d3876dbb382f9581f26134e Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 16 Aug 2020 14:09:14 +0100 Subject: [PATCH 1/7] chore: add docs for using mocks in TypeScript --- docs/GettingStarted.md | 8 +++ docs/MockFunctionAPI.md | 130 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index e5a9f42e529c..b9dd65374ec9 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -179,4 +179,12 @@ module.exports = { }; ``` +You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing [when writing your tests with TypeScript](TypeScript.md). + +> For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. + +```bash +yarn add --dev @types/jest +``` + However, there are some [caveats](https://babeljs.io/docs/en/next/babel-plugin-transform-typescript.html#caveats) to using TypeScript with Babel. Because TypeScript support in Babel is transpilation, Jest will not type-check your tests as they are run. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest). diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 563e614b8adf..7be3b468a181 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -316,3 +316,133 @@ test('async test', async () => { await asyncMock(); // throws "Async error" }); ``` + +## TypeScript + +Jest itself is written in [TypeScript](https://www.typescriptlang.org). + +If you are using [Create React App](https://create-react-app.dev) then the [TypeScript template](https://create-react-app.dev/docs/adding-typescript/) has everything you need to start writing tests in TypeScript. + +Otherwise, please see our [Getting Started](https://jestjs.io/docs/en/getting-started#using-typescript) guide for to get setup with TypeScript. + +You can see an example of using Jest with TypeScript in our [GitHub repository](https://github.com/facebook/jest/tree/master/examples/typescript). + +### `jest.MockedFunction` + +> `jest.MockedFunction` is available in the `@types/jest` module from version `24.9.0`. + +The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](https://jestjs.io/docs/en/mock-functions). + +You can use `jest.MockedFunction` to represent a function that has been replaced by a Jest mock. + +Example using [automatic `jest.mock`](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options): + +```ts +// Assume `add` is imported and used within `calculate`. +import add from './add'; +import calculate from './calc'; + +jest.mock('./add'); + +// Our mock of `add` is now fully typed +const mockAdd = add as jest.MockedFunction; + +test('calculate calls add', () => { + calculate('Add', 1, 2); + + expect(mockAdd).toBeCalledTimes(1); + expect(mockAdd).toBeCalledWith(1, 2); +}); +``` + +Example using [`jest.fn`](https://jestjs.io/docs/en/jest-object#jestfnimplementation): + +```ts +// Here `add` is imported for its type +import add from './add'; +import calculate from './calc'; + +test('calculate calls add', () => { + // Create a new mock that can be used in place of `add`. + const mockAdd = jest.fn() as jest.MockedFunction; + + // Note: You can use the `jest.fn` type directly like this if you want: + // const mockAdd = jest.fn, Parameters>(); + // `jest.MockedFunction` is a more friendly shortcut. + + // Now we can easily set up mock implementations. + // All the `.mock*` API can now give you proper types for `add`. + // https://jestjs.io/docs/en/mock-function-api + + // `.mockImplementation` can now infer that `a` and `b` are `number` + // and that the returned value is a `number`. + mockAdd.mockImplementation((a, b) => { + // Yes, this mock is still adding two numbers but imagine this + // was a complex function we are mocking. + return a + b + })); + + // `mockAdd` is properly typed and therefore accepted by + // anything requiring `add`. + calculate(mockAdd, 1 , 2); + + expect(mockAdd).toBeCalledTimes(1); + expect(mockAdd).toBeCalledWith(1, 2); +}) +``` + +### `jest.MockedClass` + +> `jest.MockedClass` is available in the `@types/jest` module from version `24.9.0`. + +The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](https://jestjs.io/docs/en/es6-class-mocks). + +You can use `jest.MockedClass` to represent a class that has been replaced by a Jest mock. + +Converting the [ES6 Class automatic mock example](https://jestjs.io/docs/en/es6-class-mocks#automatic-mock) would look like this: + +```ts +import SoundPlayer from '../sound-player'; +import SoundPlayerConsumer from '../sound-player-consumer'; + +jest.mock('../sound-player'); // SoundPlayer is now a mock constructor + +const SoundPlayerMock = SoundPlayer as jest.MockedClass; + +beforeEach(() => { + // Clear all instances and calls to constructor and all methods: + SoundPlayerMock.mockClear(); +}); + +it('We can check if the consumer called the class constructor', () => { + const soundPlayerConsumer = new SoundPlayerConsumer(); + expect(SoundPlayerMock).toHaveBeenCalledTimes(1); +}); + +it('We can check if the consumer called a method on the class instance', () => { + // Show that mockClear() is working: + expect(SoundPlayerMock).not.toHaveBeenCalled(); + + const soundPlayerConsumer = new SoundPlayerConsumer(); + // Constructor should have been called again: + expect(SoundPlayerMock).toHaveBeenCalledTimes(1); + + const coolSoundFileName = 'song.mp3'; + soundPlayerConsumer.playSomethingCool(); + + // mock.instances is available with automatic mocks: + const mockSoundPlayerInstance = SoundPlayerMock.mock.instances[0]; + + // However, it will not allow access to `.mock` in TypeScript as it + // is returning `SoundPlayer`. Instead, you can check the calls to a + // method like this fully typed: + expect(SoundPlayerMock.prototype.playSoundFile.mock.calls[0][0]).toEqual( + coolSoundFileName, + ); + // Equivalent to above check: + expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledWith( + coolSoundFileName, + ); + expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledTimes(1); +}); +``` From 139b9964b419dd3b518f01cf4755c0a6be0c839a Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Sun, 16 Aug 2020 14:38:21 +0100 Subject: [PATCH 2/7] chore: add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94312007da65..aaf73db6b16a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes ### Chore & Maintenance +- `[docs]` Add docs for using mocks in TypeScript([#10415](https://github.com/facebook/jest/pull/10415)) ### Performance From 30c3455a57f92803a0981d8507b572450c9b07f2 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Tue, 20 Oct 2020 20:14:26 +0100 Subject: [PATCH 3/7] Update CHANGELOG.md Co-authored-by: Simen Bekkhus --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf73db6b16a..2e6b3ab395b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes ### Chore & Maintenance + - `[docs]` Add docs for using mocks in TypeScript([#10415](https://github.com/facebook/jest/pull/10415)) ### Performance From 297e89e45b70e20eefbfb37cb265760aa42d5cbd Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Wed, 28 Oct 2020 21:21:15 +0000 Subject: [PATCH 4/7] chore(docs): remove unused link to TypeScript --- docs/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 3d441f44327e..fa317619a57b 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -172,7 +172,7 @@ module.exports = { However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats) to using TypeScript with Babel. Because TypeScript support in Babel is purely transpilation, Jest will not type-check your tests as they are run. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest) instead, or just run the TypeScript compiler [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html) separately (or as part of your build process). -You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing [when writing your tests with TypeScript](TypeScript.md). +You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. > For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. From 2ae3853d9b7fd8129d8baa1641b970ae02c9d7d9 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Wed, 28 Oct 2020 21:25:46 +0000 Subject: [PATCH 5/7] chore(docs): add @types/jest info to previous Getting Started docs --- website/versioned_docs/version-22.x/GettingStarted.md | 8 ++++++++ website/versioned_docs/version-23.x/GettingStarted.md | 8 ++++++++ website/versioned_docs/version-24.x/GettingStarted.md | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/website/versioned_docs/version-22.x/GettingStarted.md b/website/versioned_docs/version-22.x/GettingStarted.md index ecdff367feb8..6600ee04ce3b 100644 --- a/website/versioned_docs/version-22.x/GettingStarted.md +++ b/website/versioned_docs/version-22.x/GettingStarted.md @@ -139,3 +139,11 @@ Jest can be used in projects that use [parcel-bundler](https://parceljs.org/) to ### Using TypeScript To use TypeScript in your tests you can use [ts-jest](https://github.com/kulshekhar/ts-jest). + +You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. + +> For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. + +```bash +yarn add --dev @types/jest +``` diff --git a/website/versioned_docs/version-23.x/GettingStarted.md b/website/versioned_docs/version-23.x/GettingStarted.md index 08560199e960..b2c69ee168b8 100644 --- a/website/versioned_docs/version-23.x/GettingStarted.md +++ b/website/versioned_docs/version-23.x/GettingStarted.md @@ -147,3 +147,11 @@ Jest can be used in projects that use [parcel-bundler](https://parceljs.org/) to ### Using TypeScript To use TypeScript in your tests you can use [ts-jest](https://github.com/kulshekhar/ts-jest). + +You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. + +> For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. + +```bash +yarn add --dev @types/jest +``` diff --git a/website/versioned_docs/version-24.x/GettingStarted.md b/website/versioned_docs/version-24.x/GettingStarted.md index b2c6586b9e53..cce40437d0a8 100644 --- a/website/versioned_docs/version-24.x/GettingStarted.md +++ b/website/versioned_docs/version-24.x/GettingStarted.md @@ -172,3 +172,11 @@ module.exports = { ``` However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats) to using TypeScript with Babel. Because TypeScript support in Babel is purely transpilation, Jest will not type-check your tests as they are run. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest) instead, or just run the TypeScript compiler [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html) separately (or as part of your build process). + +You may also want to install the [`@types/jest`](https://www.npmjs.com/package/@types/jest) module for the version of Jest you're using. This will help provide full typing when writing your tests with TypeScript. + +> For `@types/*` modules it's recommended to try to match the version of the associated module. For example, if you are using `26.4.0` of `jest` then using `26.4.x` of `@types/jest` is ideal. In general, try to match the major (`26`) and minor (`4`) version as closely as possible. + +```bash +yarn add --dev @types/jest +``` From 6289cf5ac9c0bc3ed8fc680e73a38b6189885afd Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Wed, 28 Oct 2020 21:26:56 +0000 Subject: [PATCH 6/7] chore(docs): point links to relevant version --- docs/MockFunctionAPI.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index 7be3b468a181..5bb9002d4069 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -323,7 +323,7 @@ Jest itself is written in [TypeScript](https://www.typescriptlang.org). If you are using [Create React App](https://create-react-app.dev) then the [TypeScript template](https://create-react-app.dev/docs/adding-typescript/) has everything you need to start writing tests in TypeScript. -Otherwise, please see our [Getting Started](https://jestjs.io/docs/en/getting-started#using-typescript) guide for to get setup with TypeScript. +Otherwise, please see our [Getting Started](GettingStarted.md#using-typescript) guide for to get setup with TypeScript. You can see an example of using Jest with TypeScript in our [GitHub repository](https://github.com/facebook/jest/tree/master/examples/typescript). @@ -331,11 +331,11 @@ You can see an example of using Jest with TypeScript in our [GitHub repository]( > `jest.MockedFunction` is available in the `@types/jest` module from version `24.9.0`. -The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](https://jestjs.io/docs/en/mock-functions). +The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](MockFunctions.md). You can use `jest.MockedFunction` to represent a function that has been replaced by a Jest mock. -Example using [automatic `jest.mock`](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options): +Example using [automatic `jest.mock`](JestObjectAPI.md#jestmockmodulename-factory-options): ```ts // Assume `add` is imported and used within `calculate`. @@ -355,7 +355,7 @@ test('calculate calls add', () => { }); ``` -Example using [`jest.fn`](https://jestjs.io/docs/en/jest-object#jestfnimplementation): +Example using [`jest.fn`](JestObjectAPI.md#jestfnimplementation): ```ts // Here `add` is imported for its type @@ -395,11 +395,11 @@ test('calculate calls add', () => { > `jest.MockedClass` is available in the `@types/jest` module from version `24.9.0`. -The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](https://jestjs.io/docs/en/es6-class-mocks). +The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](Es6ClassMocks.md). You can use `jest.MockedClass` to represent a class that has been replaced by a Jest mock. -Converting the [ES6 Class automatic mock example](https://jestjs.io/docs/en/es6-class-mocks#automatic-mock) would look like this: +Converting the [ES6 Class automatic mock example](Es6ClassMocks.md#automatic-mock) would look like this: ```ts import SoundPlayer from '../sound-player'; From 432e1dda3c9048f95977f5707e3c46c009867904 Mon Sep 17 00:00:00 2001 From: Chris Shepherd Date: Wed, 28 Oct 2020 21:28:29 +0000 Subject: [PATCH 7/7] chore(docs): add TypeScript helpers to previous Mock Functions --- .../version-24.x/MockFunctionAPI.md | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/website/versioned_docs/version-24.x/MockFunctionAPI.md b/website/versioned_docs/version-24.x/MockFunctionAPI.md index 7b1339094340..7af3cae090f3 100644 --- a/website/versioned_docs/version-24.x/MockFunctionAPI.md +++ b/website/versioned_docs/version-24.x/MockFunctionAPI.md @@ -317,3 +317,133 @@ test('async test', async () => { await asyncMock(); // throws "Async error" }); ``` + +## TypeScript + +Jest itself is written in [TypeScript](https://www.typescriptlang.org). + +If you are using [Create React App](https://create-react-app.dev) then the [TypeScript template](https://create-react-app.dev/docs/adding-typescript/) has everything you need to start writing tests in TypeScript. + +Otherwise, please see our [Getting Started](GettingStarted.md#using-typescript) guide for to get setup with TypeScript. + +You can see an example of using Jest with TypeScript in our [GitHub repository](https://github.com/facebook/jest/tree/master/examples/typescript). + +### `jest.MockedFunction` + +> `jest.MockedFunction` is available in the `@types/jest` module from version `24.9.0`. + +The following examples will assume you have an understanding of how [Jest mock functions work with JavaScript](MockFunctions.md). + +You can use `jest.MockedFunction` to represent a function that has been replaced by a Jest mock. + +Example using [automatic `jest.mock`](JestObjectAPI.md#jestmockmodulename-factory-options): + +```ts +// Assume `add` is imported and used within `calculate`. +import add from './add'; +import calculate from './calc'; + +jest.mock('./add'); + +// Our mock of `add` is now fully typed +const mockAdd = add as jest.MockedFunction; + +test('calculate calls add', () => { + calculate('Add', 1, 2); + + expect(mockAdd).toBeCalledTimes(1); + expect(mockAdd).toBeCalledWith(1, 2); +}); +``` + +Example using [`jest.fn`](JestObjectAPI.md#jestfnimplementation): + +```ts +// Here `add` is imported for its type +import add from './add'; +import calculate from './calc'; + +test('calculate calls add', () => { + // Create a new mock that can be used in place of `add`. + const mockAdd = jest.fn() as jest.MockedFunction; + + // Note: You can use the `jest.fn` type directly like this if you want: + // const mockAdd = jest.fn, Parameters>(); + // `jest.MockedFunction` is a more friendly shortcut. + + // Now we can easily set up mock implementations. + // All the `.mock*` API can now give you proper types for `add`. + // https://jestjs.io/docs/en/mock-function-api + + // `.mockImplementation` can now infer that `a` and `b` are `number` + // and that the returned value is a `number`. + mockAdd.mockImplementation((a, b) => { + // Yes, this mock is still adding two numbers but imagine this + // was a complex function we are mocking. + return a + b + })); + + // `mockAdd` is properly typed and therefore accepted by + // anything requiring `add`. + calculate(mockAdd, 1 , 2); + + expect(mockAdd).toBeCalledTimes(1); + expect(mockAdd).toBeCalledWith(1, 2); +}) +``` + +### `jest.MockedClass` + +> `jest.MockedClass` is available in the `@types/jest` module from version `24.9.0`. + +The following examples will assume you have an understanding of how [Jest mock classes work with JavaScript](Es6ClassMocks.md). + +You can use `jest.MockedClass` to represent a class that has been replaced by a Jest mock. + +Converting the [ES6 Class automatic mock example](Es6ClassMocks.md#automatic-mock) would look like this: + +```ts +import SoundPlayer from '../sound-player'; +import SoundPlayerConsumer from '../sound-player-consumer'; + +jest.mock('../sound-player'); // SoundPlayer is now a mock constructor + +const SoundPlayerMock = SoundPlayer as jest.MockedClass; + +beforeEach(() => { + // Clear all instances and calls to constructor and all methods: + SoundPlayerMock.mockClear(); +}); + +it('We can check if the consumer called the class constructor', () => { + const soundPlayerConsumer = new SoundPlayerConsumer(); + expect(SoundPlayerMock).toHaveBeenCalledTimes(1); +}); + +it('We can check if the consumer called a method on the class instance', () => { + // Show that mockClear() is working: + expect(SoundPlayerMock).not.toHaveBeenCalled(); + + const soundPlayerConsumer = new SoundPlayerConsumer(); + // Constructor should have been called again: + expect(SoundPlayerMock).toHaveBeenCalledTimes(1); + + const coolSoundFileName = 'song.mp3'; + soundPlayerConsumer.playSomethingCool(); + + // mock.instances is available with automatic mocks: + const mockSoundPlayerInstance = SoundPlayerMock.mock.instances[0]; + + // However, it will not allow access to `.mock` in TypeScript as it + // is returning `SoundPlayer`. Instead, you can check the calls to a + // method like this fully typed: + expect(SoundPlayerMock.prototype.playSoundFile.mock.calls[0][0]).toEqual( + coolSoundFileName, + ); + // Equivalent to above check: + expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledWith( + coolSoundFileName, + ); + expect(SoundPlayerMock.prototype.playSoundFile).toHaveBeenCalledTimes(1); +}); +```