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 documentation related to auto-mocking #8099

Merged
merged 14 commits into from Mar 18, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@

### Chore & Maintenance

- `[*]` Add documentation and tests related to auto-mocking ([#8086](https://github.com/facebook/jest/pull/8099))
Copy link
Member

Choose a reason for hiding this comment

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

Can you move this up under master?

(Might have to merge in master or rebase first)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Synced with upstream master and moved the changelog line under the master heading. Thanks for the feedback and help with this @SimenB !

- `[*]` Make sure to include `d.ts` files in the tarball when building ([#8086](https://github.com/facebook/jest/pull/8086))

### Performance
Expand Down
96 changes: 96 additions & 0 deletions docs/JestObjectAPI.md
Expand Up @@ -119,6 +119,102 @@ test('implementation created by jest.genMockFromModule', () => {
});
```

This is how `genMockFromModule` will mock the follwing data types:
pedrottimark marked this conversation as resolved.
Show resolved Hide resolved

#### `Function`

A new [mock function](https://jestjs.io/docs/en/mock-functions.html) will be created. The new function will have no formal parameters and when called will return `undefined`. This functionality also applies to `async functions`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it harm the meaning to write in present tense and active voice as much as possible? For example:

Create a new mock function which has no formal parameters and returns undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think you're right about using present tense. I also don't think present tense effects the meaning or makes it unclear. Since the rest of the docs are written in present tense I think I should update this section to match. I'll work on switching everything to present tense.


#### `Class`

A new class will be created. The interface of the original class is maintained however all of the class member functions and properties will be mocked.

#### `Object`

Objects are deeply cloned. Their keys are maintained and their values are mocked.

#### `Array`

The original array is ignored and a new empty array is created.

#### `String`

A new copy of the original string is mocked.
Copy link
Contributor

Choose a reason for hiding this comment

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

This made me stop to think (which is not necessarily bad :)

By a new copy of primitive type like string or number, is the implicit assumption as value of a property?

If yes, then is it possible to summarize the behavior, which I think confirms what testers expect as one heading for all primitive types (with a few additional tests, if needed) including boolean, symbol, null, undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it makes sense to summarize all primitives under one heading since the mocking functionality is the same. Instead of a string and number header I can create a primitives header and maybe add the following tests to the docs: https://github.com/facebook/jest/blob/445e6cb9f5fddf87174e510a602cf8bc11a840b1/packages/jest-mock/src/__tests__/index.test.ts#L33-L36


#### `Number`

A new copy of the original number is mocked.

Example:

<!-- prettier-ignore -->
Copy link
Member

Choose a reason for hiding this comment

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

?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So for some reason prettier is adding these unwanted parens after the closing class bracket

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I remove js in the opening code block in the markdown file prettier does not insert the weird parenthesis. 🤔

image

Copy link
Member

Choose a reason for hiding this comment

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

Oh, it's just adding the parense. new class Bar{} and new class Bar{}() behaves the same.

Should it be newed?

I opened up prettier/prettier#5964, not sure if it's a bug or not

Copy link
Contributor Author

@mackness mackness Mar 12, 2019

Choose a reason for hiding this comment

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

Nice, I added another comment that may or may not be related to the same issue. For now I just removed the js attribute from the markdown code block and everything is formatted correctly and syntax hi-lighting seems to be working in the site preview

```js
// example.js
module.exports = {
function: function foo(a, b) {
return a + b;
},
asyncFunction: async function asyncFoo(a, b) {
const result = await a + b;
return result;
},
class: new class Bar {
constructor() {
this.array = [1, 2, 3];
}
foo() {}
},
object: {
baz: 'foo',
bar: {
fiz: 1,
buzz: [1, 2, 3],
},
},
array: [1, 2, 3],
number: 123,
string: 'baz',
};
```

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

test('should run example code', () => {
SimenB marked this conversation as resolved.
Show resolved Hide resolved
// a new mocked function with no formal arguments.
expect(example.function.name).toEqual('foo');
expect(example.function.length).toEqual(0);

// async functions are treated just like standard synchronous functions.
expect(example.asyncFunction.name).toEqual('asyncFoo');
expect(example.asyncFunction.length).toEqual(0);

// a new mocked class that maintains the original interface and mocks member functions and properties.
expect(example.class.constructor.name).toEqual('Bar');
expect(example.class.foo.name).toEqual('foo');
expect(example.class.array.length).toEqual(0);

// a deeply cloned object that maintains the original interface and mocks it's values.
expect(example.object).toEqual({
baz: 'foo',
bar: {
fiz: 1,
buzz: [],
},
});

// the original array is ignored and a new emtpy array is mocked.
expect(example.array.length).toEqual(0);

// a new copy of the original number.
expect(example.number).toEqual(123);

// a new copy of the original string.
expect(example.string).toEqual('baz');
});
```

### `jest.mock(moduleName, factory, options)`

Mocks a module with an auto-mocked version when it is being required. `factory` and `options` are optional. For example:
Expand Down
46 changes: 46 additions & 0 deletions packages/jest-mock/src/__tests__/index.test.ts
Expand Up @@ -34,6 +34,7 @@ describe('moduleMocker', () => {
expect(moduleMocker.getMetadata('banana').value).toEqual('banana');
expect(moduleMocker.getMetadata(27).value).toEqual(27);
expect(moduleMocker.getMetadata(false).value).toEqual(false);
expect(moduleMocker.getMetadata(Infinity).value).toEqual(Infinity);
});

it('does not retrieve metadata for arrays', () => {
Expand All @@ -57,6 +58,51 @@ describe('moduleMocker', () => {
expect(metadata.members).toBeUndefined();
expect(metadata.type).toEqual('null');
});

it('retrieves metadata for ES6 classes', () => {
class ClassFooMock {
bar() {}
}
const fooInstance = new ClassFooMock();
const metadata = moduleMocker.getMetadata(fooInstance);
expect(metadata.type).toEqual('object');
expect(metadata.members.constructor.name).toEqual('ClassFooMock');
});

it('retrieves synchronous function metadata', () => {
function functionFooMock() {}
const metadata = moduleMocker.getMetadata(functionFooMock);
expect(metadata.type).toEqual('function');
expect(metadata.name).toEqual('functionFooMock');
});

it('retrieves asynchronous function metadata', () => {
async function asyncFunctionFooMock() {}
const metadata = moduleMocker.getMetadata(asyncFunctionFooMock);
expect(metadata.type).toEqual('function');
expect(metadata.name).toEqual('asyncFunctionFooMock');
});

it("retrieves metadata for object literals and it's members", () => {
const metadata = moduleMocker.getMetadata({
bar: 'two',
foo: 1,
});
expect(metadata.type).toEqual('object');
expect(metadata.members.bar.value).toEqual('two');
expect(metadata.members.bar.type).toEqual('constant');
expect(metadata.members.foo.value).toEqual(1);
expect(metadata.members.foo.type).toEqual('constant');
});

it('retrieves Date object metadata', () => {
const metadata = moduleMocker.getMetadata(Date);
expect(metadata.type).toEqual('function');
expect(metadata.name).toEqual('Date');
expect(metadata.members.now.name).toEqual('now');
expect(metadata.members.parse.name).toEqual('parse');
expect(metadata.members.UTC.name).toEqual('UTC');
});
});

describe('generateFromMetadata', () => {
Expand Down
96 changes: 96 additions & 0 deletions website/versioned_docs/version-22.x/JestObjectAPI.md
Expand Up @@ -173,6 +173,102 @@ test('implementation created by jest.genMockFromModule', () => {
});
```

This is how `genMockFromModule` will mock the follwing data types:

#### `Function`

A new [mock function](https://jestjs.io/docs/en/mock-functions.html) will be created. The new function will have no formal parameters and when called will return `undefined`. This functionality also applies to `async functions`.

#### `Class`

A new class will be created. The interface of the original class is maintained however all of the class member functions and properties will be mocked.

#### `Object`

Objects are deeply cloned. Their keys are maintained and their values are mocked.

#### `Array`

The original array is ignored and a new empty array is created.

#### `String`

A new copy of the original string is mocked.

#### `Number`

A new copy of the original number is mocked.

Example:

<!-- prettier-ignore -->
```js
// example.js
module.exports = {
function: function foo(a, b) {
return a + b;
},
asyncFunction: async function asyncFoo(a, b) {
const result = await a + b;
return result;
},
class: new class Bar {
constructor() {
this.array = [1, 2, 3];
}
foo() {}
},
object: {
baz: 'foo',
bar: {
fiz: 1,
buzz: [1, 2, 3],
},
},
array: [1, 2, 3],
number: 123,
string: 'baz',
};
```

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

test('should run example code', () => {
// a new mocked function with no formal arguments.
expect(example.function.name).toEqual('foo');
expect(example.function.length).toEqual(0);

// async functions are treated just like standard synchronous functions.
expect(example.asyncFunction.name).toEqual('asyncFoo');
expect(example.asyncFunction.length).toEqual(0);

// a new mocked class that maintains the original interface and mocks member functions and properties.
expect(example.class.constructor.name).toEqual('Bar');
expect(example.class.foo.name).toEqual('foo');
expect(example.class.array.length).toEqual(0);

// a deeply cloned object that maintains the original interface and mocks it's values.
expect(example.object).toEqual({
baz: 'foo',
bar: {
fiz: 1,
buzz: [],
},
});

// the original array is ignored and a new emtpy array is mocked.
expect(example.array.length).toEqual(0);

// a new copy of the original number.
expect(example.number).toEqual(123);

// a new copy of the original string.
expect(example.string).toEqual('baz');
});
```

### `jest.mock(moduleName, factory, options)`

Mocks a module with an auto-mocked version when it is being required. `factory` and `options` are optional. For example:
Expand Down
96 changes: 96 additions & 0 deletions website/versioned_docs/version-23.x/JestObjectAPI.md
Expand Up @@ -174,6 +174,102 @@ test('implementation created by jest.genMockFromModule', () => {
});
```

This is how `genMockFromModule` will mock the follwing data types:

#### `Function`

A new [mock function](https://jestjs.io/docs/en/mock-functions.html) will be created. The new function will have no formal parameters and when called will return `undefined`. This functionality also applies to `async functions`.

#### `Class`

A new class will be created. The interface of the original class is maintained however all of the class member functions and properties will be mocked.

#### `Object`

Objects are deeply cloned. Their keys are maintained and their values are mocked.

#### `Array`

The original array is ignored and a new empty array is created.

#### `String`

A new copy of the original string is mocked.

#### `Number`

A new copy of the original number is mocked.

Example:

<!-- prettier-ignore -->
```js
// example.js
module.exports = {
function: function foo(a, b) {
return a + b;
},
asyncFunction: async function asyncFoo(a, b) {
const result = await a + b;
return result;
},
class: new class Bar {
constructor() {
this.array = [1, 2, 3];
}
foo() {}
},
object: {
baz: 'foo',
bar: {
fiz: 1,
buzz: [1, 2, 3],
},
},
array: [1, 2, 3],
number: 123,
string: 'baz',
};
```

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

test('should run example code', () => {
// a new mocked function with no formal arguments.
expect(example.function.name).toEqual('foo');
expect(example.function.length).toEqual(0);

// async functions are treated just like standard synchronous functions.
expect(example.asyncFunction.name).toEqual('asyncFoo');
expect(example.asyncFunction.length).toEqual(0);

// a new mocked class that maintains the original interface and mocks member functions and properties.
expect(example.class.constructor.name).toEqual('Bar');
expect(example.class.foo.name).toEqual('foo');
expect(example.class.array.length).toEqual(0);

// a deeply cloned object that maintains the original interface and mocks it's values.
expect(example.object).toEqual({
baz: 'foo',
bar: {
fiz: 1,
buzz: [],
},
});

// the original array is ignored and a new emtpy array is mocked.
expect(example.array.length).toEqual(0);

// a new copy of the original number.
expect(example.number).toEqual(123);

// a new copy of the original string.
expect(example.string).toEqual('baz');
});
```

### `jest.mock(moduleName, factory, options)`

Mocks a module with an auto-mocked version when it is being required. `factory` and `options` are optional. For example:
Expand Down