Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add contexts to mocks #12601

Merged
merged 17 commits into from Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

### Features

- `[jest-mock]` Add `contexts` member to mocks ([#12601](https://github.com/facebook/jest/pull/12601))
SimenB marked this conversation as resolved.
Show resolved Hide resolved
- `[babel-jest]` Export `createTransformer` function ([#12399](https://github.com/facebook/jest/pull/12399))
- `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#12363](https://github.com/facebook/jest/pull/12363), [#12376](https://github.com/facebook/jest/pull/12376))
- `[jest-circus, jest-jasmine2]` Allowed classes and functions as `describe` and `it`/`test` names ([#12484](https://github.com/facebook/jest/pull/12484))
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-cli/src/cli/args.ts
Expand Up @@ -154,7 +154,7 @@ export const options = {
},
clearMocks: {
description:
'Automatically clear mock calls, instances and results before every test. ' +
'Automatically clear mock calls, instances, contexts and results before every test. ' +
'Equivalent to calling jest.clearAllMocks() before each test.',
type: 'boolean',
},
Expand Down
Expand Up @@ -108,7 +108,7 @@ Array [
},
Object {
"initial": false,
"message": "Automatically clear mock calls, instances and results before every test?",
"message": "Automatically clear mock calls, instances, contexts and results before every test?",
"name": "clearMocks",
"type": "confirm",
},
Expand All @@ -131,7 +131,7 @@ module.exports = {
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/tmp/jest",

// Automatically clear mock calls, instances and results before every test
// Automatically clear mock calls, instances, contexts and results before every test
// clearMocks: false,

// Indicates whether the coverage information should be collected while executing the test
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-cli/src/init/questions.ts
Expand Up @@ -43,7 +43,7 @@ const defaultQuestions: Array<PromptObject> = [
{
initial: false,
message:
'Automatically clear mock calls, instances and results before every test?',
'Automatically clear mock calls, instances, contexts and results before every test?',
name: 'clearMocks',
type: 'confirm',
},
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-config/src/Descriptions.ts
Expand Up @@ -13,7 +13,7 @@ const descriptions: {[key in keyof Config.InitialOptions]: string} = {
cacheDirectory:
'The directory where Jest should store its cached dependency information',
clearMocks:
'Automatically clear mock calls, instances and results before every test',
'Automatically clear mock calls, instances, contexts and results before every test',
collectCoverage:
'Indicates whether the coverage information should be collected while executing the test',
collectCoverageFrom:
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-environment/src/index.ts
Expand Up @@ -71,7 +71,7 @@ export interface Jest {
*/
autoMockOn(): Jest;
/**
* Clears the `mock.calls`, `mock.instances` and `mock.results` properties of
* Clears the `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` properties of
* all mocks. Equivalent to calling `.mockClear()` on every mocked function.
*/
clearAllMocks(): Jest;
Expand Down
4 changes: 3 additions & 1 deletion packages/jest-mock/__typetests__/mock-functions.test.ts
Expand Up @@ -118,7 +118,9 @@ expectType<
}>
>(MockObject.mock.instances);

const returnValue = mockFn.mock.results[0];
const returnValue =

mockFn.mock.contexts[0];

expectType<'incomplete' | 'return' | 'throw'>(returnValue.type);
expectType<unknown>(returnValue.value);
Expand Down
33 changes: 33 additions & 0 deletions packages/jest-mock/src/__tests__/index.test.ts
Expand Up @@ -424,20 +424,53 @@ describe('moduleMocker', () => {
expect(fn.mock.instances[1]).toBe(instance2);
});

it('tracks context objects passed to mock calls', () => {
const fn = moduleMocker.fn();
expect(fn.mock.instances).toEqual([]);

const ctx0 = {};
fn.apply(ctx0, []);
expect(fn.mock.contexts[0]).toBe(ctx0);

const ctx1 = {};
fn.call(ctx1);
expect(fn.mock.contexts[1]).toBe(ctx1);

const ctx2 = {};
const bound2 = fn.bind(ctx2);
bound2();
expect(fn.mock.contexts[2]).toBe(ctx2);

// null context
fn.apply(null, []);
expect(fn.mock.contexts[3]).toBe(null);
fn.call(null);
expect(fn.mock.contexts[4]).toBe(null);
fn.bind(null)();
expect(fn.mock.contexts[5]).toBe(null);

// Unspecified context is `undefined` in strict mode (like in this test) and `window` otherwise.
fn();
expect(fn.mock.contexts[6]).toBe(undefined);
});

it('supports clearing mock calls', () => {
const fn = moduleMocker.fn();
expect(fn.mock.calls).toEqual([]);

fn(1, 2, 3);
expect(fn.mock.calls).toEqual([[1, 2, 3]]);
expect(fn.mock.contexts).toEqual([undefined]);

fn.mockReturnValue('abcd');

fn.mockClear();
expect(fn.mock.calls).toEqual([]);
expect(fn.mock.contexts).toEqual([]);

fn('a', 'b', 'c');
expect(fn.mock.calls).toEqual([['a', 'b', 'c']]);
expect(fn.mock.contexts).toEqual([undefined]);

expect(fn()).toEqual('abcd');
});
Expand Down
6 changes: 6 additions & 0 deletions packages/jest-mock/src/index.ts
Expand Up @@ -201,6 +201,10 @@ type MockFunctionState<T extends FunctionLike = UnknownFunction> = {
* List of all the object instances that have been instantiated from the mock.
*/
instances: Array<ReturnType<T>>;
/**
* List of all the function contexts that have been applied to calls to the mock.
*/
contexts: Array<unknown>;
matthias-ccri marked this conversation as resolved.
Show resolved Hide resolved
/**
* List of the call order indexes of the mock. Jest is indexing the order of
* invocations of all mocks in a test file. The index is starting with `1`.
Expand Down Expand Up @@ -570,6 +574,7 @@ export class ModuleMocker {
return {
calls: [],
instances: [],
contexts: [],
invocationCallOrder: [],
results: [],
};
Expand Down Expand Up @@ -636,6 +641,7 @@ export class ModuleMocker {
const mockState = mocker._ensureMockState(f);
const mockConfig = mocker._ensureMockConfig(f);
mockState.instances.push(this);
mockState.contexts.push(this);
mockState.calls.push(args);
// Create and record an "incomplete" mock result immediately upon
// calling rather than waiting for the mock to return. This avoids
Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-25.x/JestObjectAPI.md
Expand Up @@ -560,7 +560,7 @@ test('plays audio', () => {

### `jest.clearAllMocks()`

Clears the `mock.calls`, `mock.instances` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function.
Clears the `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should work with files from /docs directory. These are the ones for future releases. The docs under /website directory are for previous versions of Jest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, I'll update the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


Returns the `jest` object for chaining.

Expand Down
27 changes: 26 additions & 1 deletion website/versioned_docs/version-25.x/MockFunctionAPI.md
Expand Up @@ -79,9 +79,34 @@ mockFn.mock.instances[0] === a; // true
mockFn.mock.instances[1] === b; // true
```


### `mockFn.mock.contexts`

An array that contains the contexts for all calls of the mock function.

A context is the `this` value that a function receives when called.
The context can be set using
`Function.prototype.bind`, `Function.prototype.call` or `Function.prototype.apply`.

For example:

```js
const mockFn = jest.fn();

const boundMockFn = mockFn.bind(thisContext0);
boundMockFn('a', 'b');
mockFn.call(thisContext1, 'a', 'b');
mockFn.apply(thisContext2, ['a', 'b']);

mockFn.mock.contexts[0] === thisContext0; // true
mockFn.mock.contexts[1] === thisContext1; // true
mockFn.mock.contexts[2] === thisContext2; // true
```


### `mockFn.mockClear()`

Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions.
Clears all information stored in the [`mockFn.mock.calls`](#mockfnmockcalls), [`mockFn.mock.instances`](#mockfnmockinstances) [`mockFn.mock.contexts`](#mockfnmockcontexts) and [`mockFn.mock.results`](#mockfnmockresults) arrays. Often this is useful when you want to clean up a mocks usage data between two assertions.

Beware that `mockClear` will replace `mockFn.mock`, not just these three properties! You should, therefore, avoid assigning `mockFn.mock` to other variables, temporary or not, to make sure you don't access stale data.

Expand Down
3 changes: 3 additions & 0 deletions website/versioned_docs/version-25.x/MockFunctions.md
Expand Up @@ -69,6 +69,9 @@ expect(someMockFunction.mock.calls[0][1]).toBe('second arg');
// The return value of the first call to the function was 'return value'
expect(someMockFunction.mock.results[0].value).toBe('return value');

// The function was called with a certain `this` context: the `element` object.
expect(someMockFunction.mock.contexts[0]).toBe(element);

// This function was instantiated exactly twice
expect(someMockFunction.mock.instances.length).toBe(2);

Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-26.x/CLI.md
Expand Up @@ -140,7 +140,7 @@ Deletes the Jest cache directory and then exits without running tests. Will dele

### `--clearMocks`

Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided.
Automatically clear mock calls, instances, contexts, and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided.

### `--collectCoverageFrom=<glob>`

Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-26.x/Configuration.md
Expand Up @@ -146,7 +146,7 @@ Jest attempts to scan your dependency tree once (up-front) and cache it in order

Default: `false`

Automatically clear mock calls, instances and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided.
Automatically clear mock calls, instances, contexts and results before every test. Equivalent to calling [`jest.clearAllMocks()`](JestObjectAPI.md#jestclearallmocks) before each test. This does not remove any mock implementation that may have been provided.

### `collectCoverage` \[boolean]

Expand Down
2 changes: 1 addition & 1 deletion website/versioned_docs/version-26.x/JestObjectAPI.md
Expand Up @@ -564,7 +564,7 @@ test('plays audio', () => {

### `jest.clearAllMocks()`

Clears the `mock.calls`, `mock.instances` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function.
Clears the `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function.

Returns the `jest` object for chaining.

Expand Down