Skip to content

Commit

Permalink
feat(MockInstance): manual control of mock scopes #857
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jul 24, 2021
1 parent e298aa2 commit fc8a2ed
Show file tree
Hide file tree
Showing 47 changed files with 2,002 additions and 5,844 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
command: npm run prettier:check
- run:
name: Lint commits
command: npx commitlint --from=master
command: npx commitlint --from=origin/master
- run:
name: Lint tslint
command: npm run lint
Expand Down
64 changes: 63 additions & 1 deletion docs/articles/api/MockInstance.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,70 @@ MockInstance(TOKEN);
MockReset();
```

### Customization scopes

Time to time, we need to apply a set of customizations for a suite or a test.
To discard each customization might require writing boring resets, especially, when we have a lot of them.
In such a case, [`MockInstance.remember()`](#remember) and [`MockInstance.restore()`](#restore) come for help.

#### Remember

`MockInstance.remember()` creates a check point. Any mock customizations via `MockInstance` after the checkpoint will be recorded separately.

#### Restore

`MockInstance.restore()` discards mock customizations starting from the last known checkpoint.
The operation can be repeated unless there is a checkpoint.

#### Example

For example, we can create checkpoints in `beforeAll` or `beforeEach`,
and discard their mock customizations in `afterAll` or `afterEach`.

```ts
describe('suite', () => {
beforeAll(MockInstance.remember);
afterAll(MockInstance.restore);

// Exists only during this and all child suites.
beforeAll(() => MockInstance(SomeService, 'login$', EMPTY));
beforeAll(() => MockInstance(SomeService, 'logout$', EMPTY));

describe('sub suite', () => {
beforeEach(MockInstance.remember);
afterEach(MockInstance.restore);

// Existins only during this sub suite.
// After the sub suite, the parent
// customization will be present.
beforeEach(() => MockInstance(SomeService, 'login$', throwError('wrong')));
beforeEach(() => MockInstance(SomeService, 'logout$', throwError('wrong')));
});
});
```

#### Scope

There is `MockInstance.scope()` to reduce code to one line:

```ts
describe('suite', () => {
// Uses beforeAll and afterAll.
MockInstance.scope();

describe('sub suite', () => {
// Uses beforeEach and afterEach.
MockInstance.scope('each');
});
});
```

## Overriding customization

Every call of `MockInstance` overrides the previous callback.
`MockInstance` can be called anywhere,
but **suggested usage** is to call `MockInstance` in `beforeEach` or in `it`,
whereas the **suggested usage** is to use [`MockInstance.scope`](#scope)
and to call `MockInstance` in `beforeEach` or in `it`,
then the callback has its effect only during the current spec.

```ts
Expand Down Expand Up @@ -163,6 +222,7 @@ It accepts a class as the first parameter, and a tiny callback describing how to
// Now we can customize a mock object.
// The update$ property of the object
// will be set to EMPTY in its ctor call.
MockInstance.scope();
beforeEach(() => MockInstance(ChildComponent, 'update$', EMPTY));
```

Expand All @@ -179,6 +239,8 @@ Please, pay attention to comments in the code.

```ts
describe('MockInstance', () => {
MockInstance.scope();

// A normal setup of the TestBed, TargetComponent will be replaced
// with its mock object.
// Do not forget to return the promise of MockBuilder.
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/api/ngMocks/defaultMock.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Sets default values for mocks in the whole testing environment.
- `ngMocks.defaultMock( Component )` - removes overrides
- `ngMocks.defaultMock( TOKEN )` - removes overrides

The best place to do that is in `src/test.ts` for jasmine or in `src/setupJest.ts` for `jest`.
The best place to do that is in `src/test.ts` for jasmine or in `src/setup-jest.ts` for `jest`.

For example, if a service or component has a property that should be an `Observable`.
Then, we can configure it to be an `EMPTY` stream in the whole test suite.
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/api/ngMocks/globalExclude.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Documentation about ngMocks.globalExclude from ng-mocks library

`ngMocks.globalExclude` marks declarations, services and tokens to be excluded during creating mock modules.

The best place to do that is in `src/test.ts` for `jasmine` or in `src/setupJest.ts` for `jest`.
The best place to do that is in `src/test.ts` for `jasmine` or in `src/setup-jest.ts` for `jest`.

It is useful when some of them have been provided in `TestBed.initTestEnvironment`,
and we would like to get these versions in tests, although something declares or imports original ones.
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/api/ngMocks/globalKeep.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Documentation about ngMocks.globalKeep from ng-mocks library

`ngMocks.globalExclude` marks declarations, services and tokens to be avoided from the mocking process during creating mock modules.

The best place to do that is in `src/test.ts` for `jasmine` or in `src/setupJest.ts` for `jest`.
The best place to do that is in `src/test.ts` for `jasmine` or in `src/setup-jest.ts` for `jest`.

Let's mark the `APP_URL` token in order to be kept in mock modules.

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/api/ngMocks/globalMock.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Documentation about ngMocks.globalMock from ng-mocks library

`ngMocks.globalMock` marks declarations, services and tokens to be mocked if they appear in kept modules during creating mock modules.

The best place to do that is in `src/test.ts` for `jasmine` or in `src/setupJest.ts` for `jest`.
The best place to do that is in `src/test.ts` for `jasmine` or in `src/setup-jest.ts` for `jest`.

Let's mark the `APP_URL` token in order to be mocked in its kept modules.

Expand Down
2 changes: 1 addition & 1 deletion docs/articles/api/ngMocks/globalReplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Documentation about ngMocks.globalReplace from ng-mocks library

`ngMocks.globalReplace` marks declarations and modules (but not services and tokens) to be replaced during creating mock modules.

The best place to do that is in `src/test.ts` for `jasmine` or in `src/setupJest.ts` for `jest`.
The best place to do that is in `src/test.ts` for `jasmine` or in `src/setup-jest.ts` for `jest`.

If we wanted to replace `BrowserAnimationsModule` with `NoopAnimationsModule` globally,
we could do it like that:
Expand Down
4 changes: 2 additions & 2 deletions docs/articles/extra/auto-spy.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ ngMocks.autoSpy('jasmine');
// jasmine.getEnv().allowRespy(true);
```

In case of jest add it to `src/setupJest.ts`.
In case of jest add it to `src/setup-jest.ts`.

```ts title="src/setupJest.ts"
```ts title="src/setup-jest.ts"
import { ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jest');
Expand Down
57 changes: 57 additions & 0 deletions docs/articles/extra/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,60 @@ Simply install it as a dev dependency.
```bash npm2yarn
npm install ng-mocks --save-dev
```

## Default customizations

It may be useful to configure [auto spy](./auto-spy.md) for all methods, getters and setters in mock declarations.

Apart from [auto spy](./auto-spy.md), we may want to customize mock behavior via [MockInstance](../api/MockInstance.md).
There is a way to reset all customizations automatically on `afterEach` and `afterAll` levels.

Simply add the next code to `src/test.ts` or `src/setup-jest.ts` in case of jest:

```ts
import { ngMocks } from 'ng-mocks';

// auto spy
ngMocks.autoSpy('jasmine');
// in case of jest
// ngMocks.autoSpy('jest');

// auto restore for jasmine and jest <27
// declare const jasmine: any;
jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});

// // If you use jest v27+, please add to its config testRunner=jest-jasmine2 for now.
// // If you don't want to rely on jasmine at all, then, please,
// // upvote the issue on github: https://github.com/facebook/jest/issues/11483.
// // Once it has been merged you can use the code below.
// // Also, please consider usage of MockInstance.scope instead.
// import { addEventHandler } from 'jest-circus';
// addEventHandler((event: { name: string }) => {
// switch (event.name) {
// case 'run_describe_start':
// case 'test_start':
// MockInstance.remember();
// break;
// case 'run_describe_finish':
// case 'run_finish':
// MockInstance.restore();
// break;
// default:
// }
// });

// // in case of mocha
// mocha.setup({
// rootHooks: {
// afterAll: MockInstance.restore,
// afterEach: MockInstance.restore,
// beforeAll: MockInstance.remember,
// beforeEach: MockInstance.remember,
// },
// });
```
2 changes: 1 addition & 1 deletion docs/articles/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Also, there is a brief summary with **the latest changes** in [CHANGELOG](https:
## Very short introduction

Global configuration for mocks in `src/test.ts`.
In case of jest `src/setupJest.ts` should be used.
In case of jest `src/setup-jest.ts` should be used.

```ts title="src/test.ts"
// All methods in mock declarations and providers
Expand Down
9 changes: 9 additions & 0 deletions docs/articles/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ Below you can find critical changes. They happen on major releases.

If you are facing an issue, despite the instructions, please, feel free to [contact us](./need-help.md).

## From any old one to 12.4.0

Because of issues with the speed of merging a fix for `jest`, there is a braking change in `12.4.0`.

If you are using [`MockInstance`](./api/MockInstance.md) in `beforeAll`, `beforeEach` or `it`,
and rely on automatic reset, then you have to perform extra configuration.
More information in the [How to install ng-mocks](./extra/install.md#default-customizations)
and in [`MockInstance.scope`](./api/MockInstance.md#scope) sections.

## From 11 to 12

The only breaking change is `auto-spy`.
Expand Down
2 changes: 1 addition & 1 deletion e2e/a-jasmine/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<title>A Jasmine</title>
<title>Angular Jasmine only</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
Expand Down
10 changes: 8 additions & 2 deletions e2e/a-jasmine/src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jasmine');
declare const require: {
Expand All @@ -16,9 +16,15 @@ declare const require: {
<T>(id: string): T;
};
};

jasmine.getEnv().allowRespy(true);

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
Expand Down
9 changes: 8 additions & 1 deletion e2e/a-jest/src/setup-jest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import 'jest-preset-angular/setup-jest';

import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jest');

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});
3 changes: 0 additions & 3 deletions e2e/a-min/src/setup-jest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
import 'jest-preset-angular/setup-jest';
import { ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jest');
9 changes: 8 additions & 1 deletion e2e/a10/src/setupJest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import 'jest-preset-angular';
import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jest');

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});
9 changes: 8 additions & 1 deletion e2e/a10/src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jasmine');
declare const require: {
Expand All @@ -18,6 +18,13 @@ declare const require: {
};
jasmine.getEnv().allowRespy(true);

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
Expand Down
9 changes: 8 additions & 1 deletion e2e/a11/src/setupJest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import 'jest-preset-angular';
import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jest');

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});
9 changes: 8 additions & 1 deletion e2e/a11/src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jasmine');
declare const require: {
Expand All @@ -18,6 +18,13 @@ declare const require: {
};
jasmine.getEnv().allowRespy(true);

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
// Then we find all the tests.
Expand Down
9 changes: 8 additions & 1 deletion e2e/a12/src/setupJest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import 'jest-preset-angular';
import { ngMocks } from 'ng-mocks';
import { MockInstance, ngMocks } from 'ng-mocks';

ngMocks.autoSpy('jest');

jasmine.getEnv().addReporter({
specDone: MockInstance.restore,
specStarted: MockInstance.remember,
suiteDone: MockInstance.restore,
suiteStarted: MockInstance.remember,
});
Loading

0 comments on commit fc8a2ed

Please sign in to comment.