From f37a663761ec7a5cc122661cb91b6b82f99b5ed4 Mon Sep 17 00:00:00 2001 From: satanTime Date: Thu, 9 Jun 2022 22:30:44 +0200 Subject: [PATCH] feat(MockBuilder): default flags as dependency or export #2647 BREAKING CHANGE: MockBuilder with 2 params marks all chain calls as dependency BREAKING CHANGE: MockBuilder with 0-1 params marks all chain calls as export Please read: https://ng-mocks.sudo.eu/migrations#from-13-to-14 --- docs/articles/api/MockBuilder.md | 129 +++- docs/articles/api/MockComponent.md | 12 +- docs/articles/api/MockDirective.md | 17 +- docs/articles/api/MockInstance.md | 3 +- docs/articles/api/MockModule.md | 7 +- docs/articles/api/MockPipe.md | 13 +- docs/articles/api/MockProvider.md | 5 +- docs/articles/api/MockRender.md | 4 +- docs/articles/api/ngMocks/faster.md | 7 +- docs/articles/api/ngMocks/guts.md | 2 +- docs/articles/credits.md | 8 + docs/articles/extra/mock-form-controls.md | 14 +- docs/articles/extra/mock-observables.md | 8 +- docs/articles/extra/quick-start.md | 2 + docs/articles/extra/with-3rd-party.md | 10 +- docs/articles/guides/libraries/ngrs.md | 14 +- docs/articles/guides/libraries/ngrx.md | 24 +- docs/articles/guides/provider.md | 2 + docs/articles/guides/route.md | 46 +- docs/articles/guides/routing-guard.md | 26 +- docs/articles/guides/routing-resolver.md | 25 +- docs/articles/guides/token.md | 16 +- docs/articles/migrations.md | 65 ++ .../declarations-of-2-modules.md | 5 +- e2e/jest/jest.es2015ivy.js | 4 +- e2e/jest/jest.es5ivy.js | 4 +- examples/MockComponent/test.spec.ts | 20 +- examples/MockDirective-Attribute/test.spec.ts | 19 +- .../MockDirective-Structural/test.spec.ts | 5 +- examples/MockForms/test.spec.ts | 21 +- examples/MockInstance/test.spec.ts | 10 +- examples/MockModule/test.spec.ts | 15 +- examples/MockPipe/test.spec.ts | 9 +- examples/MockReactiveForms/test.spec.ts | 18 +- examples/MockRender/test.spec.ts | 6 +- examples/TestRoute/test.spec.ts | 20 +- examples/TestRoutingGuard/test.spec.ts | 8 +- examples/TestRoutingResolver/test.spec.ts | 11 +- examples/TestToken/test.spec.ts | 10 +- examples/ngMocksFaster/test.spec.ts | 19 +- libs/ng-mocks/src/lib/common/core.config.ts | 1 + .../lib/common/core.reflect.provided-in.ts | 6 +- libs/ng-mocks/src/lib/common/core.tokens.ts | 6 + libs/ng-mocks/src/lib/common/core.types.ts | 2 +- .../src/lib/common/ng-mocks-universe.ts | 2 + .../mock-builder.performance.spec.ts | 28 +- .../lib/mock-builder/mock-builder.promise.ts | 24 +- .../src/lib/mock-builder/mock-builder.ts | 8 +- .../mock-builder/promise/init-ng-modules.ts | 42 +- .../mock-builder/promise/try-mock-provider.ts | 2 +- .../src/lib/mock-builder/promise/types.ts | 2 + libs/ng-mocks/src/lib/mock-builder/types.ts | 2 +- .../mock-helper.find-instance.ts | 2 +- .../mock-helper.find-instances.ts | 2 +- .../src/lib/mock-helper/func.get-from-node.ts | 2 +- .../src/lib/mock-helper/mock-helper.faster.ts | 2 + .../src/lib/mock-helper/mock-helper.object.ts | 5 +- .../src/lib/mock-helper/mock-helper.ts | 1 + .../src/issue-312/import-features.spec.ts | 21 +- tests-e2e/src/issue-312/only-feature.spec.ts | 11 +- tests-e2e/src/issue-488/faster.spec.ts | 16 +- tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts | 2 + tests/auto-spy/test.spec.ts | 7 +- tests/flex-exact-mocks/test.spec.ts | 19 +- tests/interceptor-kept-mocked/test.spec.ts | 14 +- tests/internal-only-nested/test.spec.ts | 2 +- tests/issue-1256/test.spec.ts | 6 +- tests/issue-151/test.spec.ts | 12 +- tests/issue-162/test.spec.ts | 4 +- tests/issue-186/test.spec.ts | 6 +- tests/issue-197/abstract.spec.ts | 5 +- tests/issue-197/with-providers.spec.ts | 10 +- tests/issue-222/application-module.spec.ts | 5 +- tests/issue-222/injector-scope.spec.ts | 1 + tests/issue-222/kept-root-injection.spec.ts | 12 +- tests/issue-2647/errors.spec.ts | 434 ++++++++++++++ tests/issue-2647/ignore.spec.ts | 543 +++++++++++++++++ tests/issue-2647/test.spec.ts | 64 ++ tests/issue-2647/warn.spec.ts | 561 ++++++++++++++++++ tests/issue-312/test.spec.ts | 1 + tests/issue-333/test.spec.ts | 14 +- tests/issue-455/abstract.spec.ts | 1 + tests/issue-488/test.spec.ts | 4 +- tests/issue-572/test.spec.ts | 9 +- tests/issue-623/nested.spec.ts | 4 +- tests/issue-625/test.spec.ts | 23 +- tests/issue-762/string.spec.ts | 17 +- .../test.spec.ts | 13 +- tests/mock-instance-in-it/test.spec.ts | 9 +- tests/mock-instance-token/test.spec.ts | 5 +- tests/module-with-factory-tokens/test.spec.ts | 26 +- .../test.precise.spec.ts | 5 +- .../ng-mocks-default-mock/test.unset.spec.ts | 4 +- tests/providedin-root/test.spec.ts | 1 + .../test.spec.ts | 20 +- .../root-provider-with-root-dep/test.spec.ts | 5 +- tests/root-providers/fixtures.ts | 2 + tests/root-providers/test.spec.ts | 12 +- tests/tokens-class/test.spec.ts | 9 +- tests/tokens-existing/test.spec.ts | 5 +- tests/tokens-factory/test.spec.ts | 7 +- tests/tokens-value/test.spec.ts | 13 +- 102 files changed, 2380 insertions(+), 386 deletions(-) create mode 100644 tests/issue-2647/errors.spec.ts create mode 100644 tests/issue-2647/ignore.spec.ts create mode 100644 tests/issue-2647/test.spec.ts create mode 100644 tests/issue-2647/warn.spec.ts diff --git a/docs/articles/api/MockBuilder.md b/docs/articles/api/MockBuilder.md index e26b78fdf6..0b88449f08 100644 --- a/docs/articles/api/MockBuilder.md +++ b/docs/articles/api/MockBuilder.md @@ -11,15 +11,21 @@ but with minimum overhead. Usually, we have something simple to test, but time to time, the simplicity is killed by nightmarish dependencies. The good thing here is that commonly the dependencies have been declared or imported in the same module, where our tested thing is. Therefore, with help of `MockBuilder` we can quite easily define a testing module, -where **everything in the module will be replaced with their mocks**, except the tested thing: `MockBuilder( TheThing, ItsModule )`. +where **everything in the module will be replaced with their mocks**, except the tested thing: -MockBuilder tends to provide **a simple instrument to turn Angular dependencies into their mocks**, +```ts +beforeEach(() => { + return MockBuilder(TheThing, ItsModule); +}); +``` + +`MockBuilder` tends to provide **a simple instrument to turn Angular dependencies into their mocks**, does it in isolated scopes, and has a rich toolkit that supports: - detection and creation of mocks for root providers -- replacement of modules and declarations in any depth -- exclusion of modules, declarations and providers in any depth +- replacement of modules and declarations at any depth +- exclusion of modules, declarations and providers at any depth ## Simple example @@ -50,6 +56,94 @@ describe('MockBuilder:simple', () => { }); ``` +## Flex mode + +You can use the flex mode to build TestBed in the way you want. +Let's assume you want to test `TargetComponent` and it has 3 dependencies: + +- `CurrencyPipe` should be a mock +- `TimeService` should be a mock +- `ReactiveFormModule` should stay as it is + +For this case, `MockBuilder` can be called like that: + +```ts +beforeEach(() => { + return MockBuilder() + // It will be declared as it is in the TestBed. + .keep(TargetComponent) + + // It will be declared as a mock in the TestBed. + .mock(CurrencyPipe) + + // It will be provided as a mock in the TestBed. + .mock(TimeService) + + // It will be imported as it is in the TestBed. + .keep(ReactiveFormModule); +}); +``` + +This approach is good, however the problem is that dependencies are provided explicitly, +and if someone has removed `ReactiveFormModule` from the module where `TargetComponent` has been declared, +the test won't fail, whereas the app will. + +There is where the [strict mode](#strict-mode) shines. + +## Strict mode + +The strict mode is enabled if you pass 2 parameters to `MockBuilder`: + +- the first parameter is what should be provided and kept as it is for testing +- the second parameter is what should be provided and mocked for testing +- the chain calls only customize these declarations + +If we consider the example from the [flex mode](#flex-mode), then, to enable the strict mode, the code should look like: + +```ts +beforeEach(() => { + // TargetComponent is exported as it is from TargetModule + // all imports and declarations of TargetModule should be mocked + return MockBuilder(TargetComponent, TargetModule) + + // It marks ReactiveFormModule to be kept as it is in TargetModule + // and throw an error if TargetModule or its imports don't import it. + .keep(ReactiveFormModule); +}); +``` + +All dependencies of `TargetComponent` are in `TargetModule`, and if any of them have been deleted, tests will fail. + +Also, if someone has deleted `ReactiveFormModule` from `TargetModule`, tests will fail too, +because `MockBuilder` will throw an error about missing `ReactiveFormModule` which should be kept. + +However, what if more than 1 module is requires? For example, for lazy modules. +In the case of lazy loaded modules, you need to import more than 1 module in TestBed. +Usually, it's an `AppModule` which provides root declarations, and a `LazyModule` which belongs to a specific route. +In order to do so, simply pass arrays as parameters of `MockBuilder`: + +```ts +beforeEach(() => { + return MockBuilder( + // It can be an array too, if you want to keep and export more than 1 thing + TargetComponent, + + [ + // It will mock and import TargetModule in TestBed + TargetModule, + // It will mock and import AppModule in TestBed + AppModule, + ], + ) + + // It will keep CurrencyPipe as it is, + // and throw if neither TargetModule nor AppModule declares or imports it. + .keep(CurrencyPipe); +}); +``` + +**The strict mode is the recommended approach**. + ## Chain functions ### .keep() @@ -58,7 +152,7 @@ If we want to keep a module, component, directive, pipe or provider as it is. We ```ts beforeEach(() => { - return MockBuilder(MyComponent, MyModule) + return MockBuilder(MyComponent) .keep(SomeModule) .keep(SomeModule.forSome()) .keep(SomeModule.forAnother()) @@ -76,7 +170,7 @@ If we want to turn anything into a mock object, even a part of a kept module we ```ts beforeEach(() => { - return MockBuilder(MyComponent, MyModule) + return MockBuilder(MyComponent) .mock(SomeModule) .mock(SomeModule.forSome()) .mock(SomeModule.forAnother()) @@ -92,7 +186,7 @@ For pipes, we can set their handlers as the 2nd parameter of `.mock`. ```ts beforeEach(() => { - return MockBuilder(MyComponent, MyModule) + return MockBuilder(MyComponent) .mock(SomePipe, value => 'My Custom Content'); }); ``` @@ -102,7 +196,7 @@ Please keep in mind that the mock object of the service will be extended with th ```ts beforeEach(() => { - return MockBuilder(MyComponent, MyModule) + return MockBuilder(MyComponent) .mock(SomeService3, anything1) .mock(SOME_TOKEN, anything2); }); @@ -153,7 +247,7 @@ In case of `RouterTestingModule` we need to use [`.keep`](#keep) for both of the ```ts beforeEach(() => { - return MockBuilder(MyComponent, MyModule) + return MockBuilder(MyComponent) .keep(RouterModule) .keep(RouterTestingModule.withRoutes([])); }); @@ -213,7 +307,7 @@ beforeEach(() => { If we want to test a component, directive or pipe which, unfortunately, has not been exported, then we need to mark it with the `export` flag. -Does not matter how deep it is. It will be exported to the level of `TestingModule`. +Does not matter how deep it is. It will be exported to the level of `MyModule`. ```ts beforeEach(() => { @@ -248,16 +342,17 @@ beforeEach(() => { ### `dependency` flag -By default, all definitions are added to the `TestingModule` if they are not a dependency of another definition. -Modules are added as imports to the `TestingModule`. -Components, Directive, Pipes are added as declarations to the `TestingModule`. -Tokens and Services are added as providers to the `TestingModule`. -If we do not want something to be added to the `TestingModule` at all, then we need to mark it with the `dependency` flag. +By default, all definitions are added to the `MyModule` if they are not a dependency of another definition. +Modules are added as imports to the `MyModule`. +Components, Directive, Pipes are added as declarations to the `MyModule`. +Tokens and Services are added as providers to the `MyModule`. +If we do not want something to be added to the `MyModule` at all, then we need to mark it with the `dependency` flag. ```ts beforeEach(() => { return ( - MockBuilder(MyComponent, MyModule) + MockBuilder(MyComponent) + .mock(MyModule) .keep(SomeModuleComponentDirectivePipeProvider1, { dependency: true, }) @@ -394,7 +489,7 @@ const ngModule = MockBuilder() .build(); ``` -Also, we can suppress the first parameter with `null` if we want to create mocks for all declarations. +Also, we can suppress the first parameter with `null` or `undefined` if we want to create mocks for all declarations. ```ts const ngModule = MockBuilder(null, MyModule) diff --git a/docs/articles/api/MockComponent.md b/docs/articles/api/MockComponent.md index fd981b5d50..f68d307ccf 100644 --- a/docs/articles/api/MockComponent.md +++ b/docs/articles/api/MockComponent.md @@ -84,7 +84,7 @@ and [`MockRender`](MockRender.md): ```ts describe('Test', () => { beforeEach(() => { - return MockBuilder(TargetComponent).mock(DependencyComponent); + return MockBuilder(TargetComponent, ItsModule); }); it('should create', () => { @@ -105,11 +105,11 @@ Please, pay attention to comments in the code. ```ts title="https://github.com/ike18t/ng-mocks/blob/master/examples/MockComponent/test.spec.ts" describe('MockComponent', () => { beforeEach(() => { - return MockBuilder(TestedComponent).mock(DependencyComponent); + return MockBuilder(MyComponent, ItsModule); }); it('sends the correct value to the child input', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -123,7 +123,7 @@ describe('MockComponent', () => { ).componentInstance; // Let's pretend that DependencyComponent has 'someInput' as - // an input. TestedComponent sets its value via + // an input. MyComponent sets its value via // `[someInput]="value"`. The input's value will be passed into // the mock component so we can assert on it. component.value = 'foo'; @@ -134,7 +134,7 @@ describe('MockComponent', () => { }); it('does something on an emit of the child component', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -145,7 +145,7 @@ describe('MockComponent', () => { const mockComponent = ngMocks.findInstance(DependencyComponent); // Again, let's pretend DependencyComponent has an output - // called 'someOutput'. TestedComponent listens on the output via + // called 'someOutput'. MyComponent listens on the output via // `(someOutput)="trigger($event)"`. // Let's install a spy and trigger the output. ngMocks.stubMember( diff --git a/docs/articles/api/MockDirective.md b/docs/articles/api/MockDirective.md index a0c2cdec6f..d2378b244f 100644 --- a/docs/articles/api/MockDirective.md +++ b/docs/articles/api/MockDirective.md @@ -84,7 +84,8 @@ and [`MockRender`](MockRender.md): ```ts describe('Test', () => { beforeEach(() => { - return MockBuilder(TargetComponent).mock(DependencyDirective); + // DependencyDirective is a declaration in ItsModule. + return MockBuilder(TargetComponent, ItsModule); }); it('should create', () => { @@ -105,11 +106,12 @@ Please, pay attention to comments in the code. ```ts title="https://github.com/ike18t/ng-mocks/blob/master/examples/MockDirective-Attribute/test.spec.ts" describe('MockDirective:Attribute', () => { beforeEach(() => { - return MockBuilder(TestedComponent).mock(DependencyDirective); + // DependencyDirective is a declaration in ItsModule. + return MockBuilder(MyComponent, ItsModule); }); it('sends the correct value to the input', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -123,7 +125,7 @@ describe('MockDirective:Attribute', () => { ); // Let's pretend DependencyDirective has 'someInput' - // as an input. TestedComponent sets its value via + // as an input. MyComponent sets its value via // `[someInput]="value"`. The input's value will be passed into // the mock directive so we can assert on it. component.value = 'foo'; @@ -134,7 +136,7 @@ describe('MockDirective:Attribute', () => { }); it('does something on an emit of the child directive', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -148,7 +150,7 @@ describe('MockDirective:Attribute', () => { ); // Again, let's pretend DependencyDirective has an output called - // 'someOutput'. TestedComponent listens on the output via + // 'someOutput'. MyComponent listens on the output via // `(someOutput)="trigger()"`. // Let's install a spy and trigger the output. ngMocks.stubMember( @@ -184,7 +186,8 @@ describe('MockDirective:Structural', () => { // Usually a developer knows the context and can render it // manually with proper setup. beforeEach(() => { - return MockBuilder(TargetComponent, TargetModule).mock( + // DependencyDirective is a declaration in ItsModule. + return MockBuilder(TargetComponent, ItsModule).mock( DependencyDirective, { // render: true, // <-- a flag to render the directive by default diff --git a/docs/articles/api/MockInstance.md b/docs/articles/api/MockInstance.md index c8555d3d24..b509a7eca1 100644 --- a/docs/articles/api/MockInstance.md +++ b/docs/articles/api/MockInstance.md @@ -253,7 +253,8 @@ describe('MockInstance', () => { // A normal setup of the TestBed, TargetComponent will be replaced // with its mock object. // Do not forget to return the promise of MockBuilder. - beforeEach(() => MockBuilder(RealComponent).mock(ChildComponent)); + // ChildComponent is declaration of ItsModule. + beforeEach(() => MockBuilder(RealComponent, ItsModule)); beforeEach(() => { // Because TargetComponent is replaced with its mock object, diff --git a/docs/articles/api/MockModule.md b/docs/articles/api/MockModule.md index 52cb82f0de..ec7200c1f5 100644 --- a/docs/articles/api/MockModule.md +++ b/docs/articles/api/MockModule.md @@ -107,11 +107,12 @@ Please, pay attention to comments in the code. ```ts title="https://github.com/ike18t/ng-mocks/blob/master/examples/MockModule/test.spec.ts" describe('MockModule', () => { beforeEach(() => { - return MockBuilder(TestedComponent).mock(DependencyModule); + // DependencyModule is an import of ItsModule. + return MockBuilder(MyComponent, ItsModule); }); - it('renders TestedComponent with its dependencies', () => { - const fixture = MockRender(TestedComponent); + it('renders MyComponent with its dependencies', () => { + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; expect(component).toBeTruthy(); diff --git a/docs/articles/api/MockPipe.md b/docs/articles/api/MockPipe.md index ad34ddd3c9..c1c1540907 100644 --- a/docs/articles/api/MockPipe.md +++ b/docs/articles/api/MockPipe.md @@ -79,7 +79,8 @@ and call [`MockRender`](MockRender.md): ```ts describe('Test', () => { beforeEach(() => { - return MockBuilder(TargetComponent) + return MockBuilder(TargetComponent, ItsModule) + // DependencyPipe is a declaration in ItsModule .mock(DependencyPipe, value => `mock:${value}`); }); @@ -115,10 +116,12 @@ describe('MockPipe', () => { // const spy = jest.fn().mockImplementation(fakeTransform); beforeEach(() => { - return MockBuilder(TargetComponent, TargetModule).mock( - DependencyPipe, - spy, - ); + return MockBuilder(TargetComponent, ItsModule) + // DependencyPipe is a declaration in ItsModule + .mock( + DependencyPipe, + spy, + ); }); it('transforms values to json', () => { diff --git a/docs/articles/api/MockProvider.md b/docs/articles/api/MockProvider.md index 03bdf53ff1..61fa581182 100644 --- a/docs/articles/api/MockProvider.md +++ b/docs/articles/api/MockProvider.md @@ -212,8 +212,9 @@ and call [`MockRender`](MockRender.md): ```ts describe('Test', () => { beforeEach(() => { - return MockBuilder(TargetComponent) - .mock(DependencyService) + // DependencyService is a provider in ItsModule. + return MockBuilder(TargetComponent, ItsModule) + // ObservableService is a provider in ItsModule, which we need to customize .mock(ObservableService, { prop$: EMPTY, getStream$: () => EMPTY, diff --git a/docs/articles/api/MockRender.md b/docs/articles/api/MockRender.md index 69fce3f3f0..b9b0614676 100644 --- a/docs/articles/api/MockRender.md +++ b/docs/articles/api/MockRender.md @@ -540,7 +540,7 @@ Please, pay attention to comments in the code. ```ts title="https://github.com/ike18t/ng-mocks/blob/master/examples/MockRender/test.spec.ts" describe('MockRender', () => { // Do not forget to return the promise of MockBuilder. - beforeEach(() => MockBuilder(TestedComponent, DependencyModule)); + beforeEach(() => MockBuilder(MyComponent, DependencyModule)); it('renders template', () => { const spy = jasmine.createSpy(); @@ -586,7 +586,7 @@ describe('MockRender', () => { // Generates a template like: // . - const fixture = MockRender(TestedComponent, { + const fixture = MockRender(MyComponent, { trigger: spy, value1: 'something2', }); diff --git a/docs/articles/api/ngMocks/faster.md b/docs/articles/api/ngMocks/faster.md index 6748d85eb5..a0f53201b0 100644 --- a/docs/articles/api/ngMocks/faster.md +++ b/docs/articles/api/ngMocks/faster.md @@ -47,7 +47,8 @@ describe('beforeEach:mock-instance', () => { // A normal setup of the TestBed, TargetService will be replaced // with its mock object. // Do not forget to return the promise of MockBuilder. - beforeEach(() => MockBuilder(TargetComponent).mock(TargetService)); + // TargetService is a provider in ItsModule. + beforeEach(() => MockBuilder(TargetComponent, ItsModule)); // Configuring behavior of the mock TargetService. beforeAll(() => { @@ -92,7 +93,9 @@ describe('beforeEach:manual-spy', () => { // A normal setup of the TestBed, TargetService will be replaced // with its mock object. beforeEach(() => { - return MockBuilder(TargetComponent).mock(TargetService, mock); + return MockBuilder(TargetComponent, ItsModule) + // TargetService is a provider in ItsModule. + .mock(TargetService, mock); }); }); ``` diff --git a/docs/articles/api/ngMocks/guts.md b/docs/articles/api/ngMocks/guts.md index f872ca9ed0..5a94d80e60 100644 --- a/docs/articles/api/ngMocks/guts.md +++ b/docs/articles/api/ngMocks/guts.md @@ -5,7 +5,7 @@ description: Documentation about ngMocks.guts from ng-mocks library Generates and returns metadata for `TestBed` module. -- `ngMocks.guts( TestingDeclaration, ItsModule )` +- `ngMocks.guts( MyDeclaration, ItsModule )` - `ngMocks.guts( [Thing1, Thing2], [ToMock1, ToMock2], [Skip1, Skip2] )` The first parameter can be a declaration or array of them which we want to test. diff --git a/docs/articles/credits.md b/docs/articles/credits.md index bc10513c39..859e06dc7a 100644 --- a/docs/articles/credits.md +++ b/docs/articles/credits.md @@ -13,6 +13,14 @@ sidebar_label: Credits ## Supporters +- [DmitryEfimenko](https://github.com/DmitryEfimenko) + provided valuable feedback and ideas how to improve the library +- [philmayfield](https://github.com/philmayfield) + donated to support the development +- [patelvimal](https://github.com/patelvimal) + donated to support the development +- [qdelettre](https://github.com/qdelettre) + donated to support the development - [santoshyadavdev](https://github.com/santoshyadavdev) donated to support the development - [SerkanSipahi](https://github.com/SerkanSipahi) diff --git a/docs/articles/extra/mock-form-controls.md b/docs/articles/extra/mock-form-controls.md index 6ec73be4fb..9d4e761678 100644 --- a/docs/articles/extra/mock-form-controls.md +++ b/docs/articles/extra/mock-form-controls.md @@ -91,8 +91,9 @@ describe('MockReactiveForms', () => { MockInstance.scope(); beforeEach(() => { - return MockBuilder(TestedComponent) - .mock(DependencyComponent) + // DependencyComponent is a declaration in ItsModule. + return MockBuilder(MyComponent, ItsModule) + // ReactiveFormsModule is an import in ItsModule. .keep(ReactiveFormsModule); }); @@ -107,7 +108,7 @@ describe('MockReactiveForms', () => { // the spy via MockInstance before the render. MockInstance(DependencyComponent, 'writeValue', writeValue); - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // During initialization it should be called @@ -139,8 +140,9 @@ describe('MockForms', () => { MockInstance.scope(); beforeEach(() => { - return MockBuilder(TestedComponent) - .mock(DependencyComponent) + // DependencyComponent is a declaration in ItsModule. + return MockBuilder(MyComponent, ItsModule) + // FormsModule is an import in ItsModule. .keep(FormsModule); }); @@ -155,7 +157,7 @@ describe('MockForms', () => { // the spy via MockInstance before the render. MockInstance(DependencyComponent, 'writeValue', writeValue); - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); // FormsModule needs fixture.whenStable() // right after MockRender to install all hooks. await fixture.whenStable(); diff --git a/docs/articles/extra/mock-observables.md b/docs/articles/extra/mock-observables.md index 5582949dc8..e80b56f7e2 100644 --- a/docs/articles/extra/mock-observables.md +++ b/docs/articles/extra/mock-observables.md @@ -114,9 +114,11 @@ let todoServiceList$: Subject; // <- a context variable. beforeEach(() => { todoServiceList$ = new Subject(); // <- create the subject. - return MockBuilder(TodoComponent).mock(TodoService, { - list$: () => todoServiceList$, - }); + return MockBuilder(TodoComponent, ItsModule) + // TodoService is provided in ItsModule + .mock(TodoService, { + list$: () => todoServiceList$, + }); }); it('test', () => { diff --git a/docs/articles/extra/quick-start.md b/docs/articles/extra/quick-start.md index be049b92a2..c943f3a9c1 100644 --- a/docs/articles/extra/quick-start.md +++ b/docs/articles/extra/quick-start.md @@ -273,7 +273,9 @@ the code would look like: ```ts beforeEach(() => { return MockBuilder(AppBaseComponent, AppBaseModule) + // TranslatePipe is declarared / imported in AppBaseModule .mock(TranslatePipe, v => `translated:${v}`) + // SearchService is provided / imported in AppBaseModule .mock(SearchService, { result$: EMPTY, }); diff --git a/docs/articles/extra/with-3rd-party.md b/docs/articles/extra/with-3rd-party.md index 5b4f1f2c1b..8801d59502 100644 --- a/docs/articles/extra/with-3rd-party.md +++ b/docs/articles/extra/with-3rd-party.md @@ -30,11 +30,8 @@ const createComponent = createComponentFactory({ If we use [`MockBuilder`](../api/MockBuilder.md) we need [`.exclude`](../api/MockBuilder.md#exclude), [`.mock`](../api/MockBuilder.md#mock) and [`exportAll`](../api/MockBuilder.md#exportall-flag) flag. ```ts -const dependencies = MockBuilder() +const dependencies = MockBuilder(null, MyModule) .exclude(MyComponent) - .mock(MyModule, { - exportAll: true, - }) .build(); const createComponent = createComponentFactory({ @@ -59,11 +56,8 @@ await render(MyComponent, dependencies); In case of [`MockBuilder`](../api/MockBuilder.md): ```ts -const dependencies = MockBuilder() +const dependencies = MockBuilder(null, MyModule) .exclude(MyComponent) - .mock(MyModule, { - exportAll: true, - }) .build(); await render(MyComponent, dependencies); ``` diff --git a/docs/articles/guides/libraries/ngrs.md b/docs/articles/guides/libraries/ngrs.md index de96510e16..1118fdcb8c 100644 --- a/docs/articles/guides/libraries/ngrs.md +++ b/docs/articles/guides/libraries/ngrs.md @@ -8,8 +8,10 @@ If you need to avoid mocking of `NGRS` in your modules, you need to use [`.keep` ```ts beforeEach(() => MockBuilder(TargetComponent, TargetModule) + // NgxsModule.forRoot() is called in TargetModule or its imports .keep(NgxsModule.forRoot().ngModule) // keeps all NgxsModule.forRoot // add it only if your module imports NgxsModule.forFeature + // NgxsModule.forFeature() is called in TargetModule or its imports .keep(NgxsModule.forFeature().ngModule) // keeps all NgxsModule.forFeature ); ``` @@ -18,8 +20,14 @@ if your module imports `NgxsModule.forFeature` only, you need to add `NgxsModule ```ts beforeEach(() => - MockBuilder(TargetComponent, TargetModule) - .keep(NgxsModule.forRoot()) // provides required services - .keep(NgxsModule.forFeature().ngModule) // keeps all NgxsModule.forFeature + MockBuilder( + // keep and export + [ + TargetComponent, + NgxsModule.forRoot(), // provides required services + ], + // mock + TargetModule, + ).keep(NgxsModule.forFeature().ngModule) // keeps all NgxsModule.forFeature ); ``` diff --git a/docs/articles/guides/libraries/ngrx.md b/docs/articles/guides/libraries/ngrx.md index 54c5c1f098..9bfbefd2ba 100644 --- a/docs/articles/guides/libraries/ngrx.md +++ b/docs/articles/guides/libraries/ngrx.md @@ -34,15 +34,21 @@ you need to add `.forRoot()` manually: ```ts beforeEach(() => - MockBuilder(SomeComponent, LazyLoadedModule) - - // providing root tools - .keep(StoreModule.forRoot({})) - .keep(EffectsModule.forRoot()) - - // keeping lazy loaded module imports - .keep(StoreFeatureModule) // keeps all StoreModule.forFeature - .keep(EffectsFeatureModule) // keeps all EffectsModule.forFeature + MockBuilder( + // keep and export + [ + SomeComponent, + // providing root tools + StoreModule.forRoot({}), + EffectsModule.forRoot(), + ], + // mock + LazyLoadedModule, + ) + + // keeping lazy loaded module imports + .keep(StoreFeatureModule) // keeps all StoreModule.forFeature + .keep(EffectsFeatureModule) // keeps all EffectsModule.forFeature ); ``` diff --git a/docs/articles/guides/provider.md b/docs/articles/guides/provider.md index 847dc46d40..ef56cbffca 100644 --- a/docs/articles/guides/provider.md +++ b/docs/articles/guides/provider.md @@ -27,9 +27,11 @@ There are 3 options: `.mock`, `.provide` and `MockInstance`. All of them are a g ```ts beforeEach(() => MockBuilder(TargetService, TargetModule) + // Service2 is provided / imported in TargetModule .mock(Service2, { trigger: () => 'mock2', }) + // Service3 will be provided in TestBed .provide({ provide: Service3, useValue: { diff --git a/docs/articles/guides/route.md b/docs/articles/guides/route.md index e21876813a..69a483690f 100644 --- a/docs/articles/guides/route.md +++ b/docs/articles/guides/route.md @@ -15,8 +15,14 @@ This guarantees that the application routes will be used, and tests fail if a ro ```ts beforeEach(() => - MockBuilder(RouterModule, TargetModule).keep( - RouterTestingModule.withRoutes([]) + MockBuilder( + // Things to keep and export. + [ + RouterModule, + RouterTestingModule.withRoutes([]), + ], + // Things to mock. + TargetModule, ) ); ``` @@ -60,9 +66,16 @@ to `.keep` `RouterModule` and to render the component instead of `RouterOutlet`. ```ts beforeEach(() => - MockBuilder(TargetComponent, TargetModule) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])) + MockBuilder( + // Things to keep and export. + [ + TargetComponent, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + // Things to mock. + TargetModule, + ) ); ``` @@ -163,8 +176,12 @@ class TargetModule {} describe('TestRoute:Route', () => { beforeEach(() => { - return MockBuilder(RouterModule, TargetModule).keep( - RouterTestingModule.withRoutes([]), + return MockBuilder( + [ + RouterModule, + RouterTestingModule.withRoutes([]), + ], + TargetModule, ); }); @@ -214,9 +231,14 @@ describe('TestRoute:Component', () => { // RouterTestingModule.withRoutes([]), yes yes, with empty routes // to have tools for testing. beforeEach(() => { - return MockBuilder(TargetComponent, TargetModule) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])); + return MockBuilder( + [ + TargetComponent, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + TargetModule, + ); }); it('navigates between pages', fakeAsync(() => { @@ -230,8 +252,8 @@ describe('TestRoute:Component', () => { tick(); // is needed for rendering of the current route. } - // By default our routes do not have a component. - // Therefore non of them should be rendered. + // By default, our routes do not have a component. + // Therefore, none of them should be rendered. expect(location.path()).toEqual('/'); expect(() => ngMocks.find(Target1Component)).toThrow(); expect(() => ngMocks.find(Target2Component)).toThrow(); diff --git a/docs/articles/guides/routing-guard.md b/docs/articles/guides/routing-guard.md index 7c4d7c1b57..efb0f90fad 100644 --- a/docs/articles/guides/routing-guard.md +++ b/docs/articles/guides/routing-guard.md @@ -13,10 +13,16 @@ excluded from `TestBed` and we can be sure, that we are **testing only the guard ```ts beforeEach(() => - MockBuilder(LoginGuard, TargetModule) - .exclude(NG_MOCKS_GUARDS) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])) + MockBuilder( + // Things to keep and export. + [ + LoginGuard, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + // Things to mock + TargetModule, + ).exclude(NG_MOCKS_GUARDS) ); ``` @@ -159,10 +165,14 @@ describe('TestRoutingGuard', () => { // to have tools for testing. And the last thing is to exclude // `NG_MOCKS_GUARDS` to remove all other guards. beforeEach(() => { - return MockBuilder(LoginGuard, TargetModule) - .exclude(NG_MOCKS_GUARDS) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])); + return MockBuilder( + [ + LoginGuard, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + TargetModule, + ).exclude(NG_MOCKS_GUARDS); }); // It is important to run routing tests in fakeAsync. diff --git a/docs/articles/guides/routing-resolver.md b/docs/articles/guides/routing-resolver.md index fe0c4cb4ea..156b865b8a 100644 --- a/docs/articles/guides/routing-resolver.md +++ b/docs/articles/guides/routing-resolver.md @@ -11,10 +11,16 @@ Optionally, we can disable guards to avoid influence of their mocked methods ret ```ts beforeEach(() => - MockBuilder(DataResolver, TargetModule) - .exclude(NG_MOCKS_GUARDS) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])) + MockBuilder( + // Things to keep and export. + [ + DataResolver, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + // Things to mock. + TargetModule, + ).exclude(NG_MOCKS_GUARDS) ); ``` @@ -156,9 +162,14 @@ describe('TestRoutingResolver', () => { // add RouterTestingModule.withRoutes([]), yes yes, with empty // routes to have tools for testing. beforeEach(() => { - return MockBuilder(DataResolver, TargetModule) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])); + return MockBuilder( + [ + DataResolver, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + TargetModule, + ); }); // It is important to run routing tests in fakeAsync. diff --git a/docs/articles/guides/token.md b/docs/articles/guides/token.md index 3eda56ba35..efad9f73c1 100644 --- a/docs/articles/guides/token.md +++ b/docs/articles/guides/token.md @@ -22,7 +22,9 @@ been marked for being kept. ```ts beforeEach(() => - MockBuilder(TOKEN_EXISTING, TargetModule).keep(ServiceExisting) + MockBuilder(TOKEN_EXISTING, TargetModule) + // ServiceExisting is provided / imported in TargetModule + .keep(ServiceExisting) ); ``` @@ -89,12 +91,12 @@ describe('TestToken', () => { // initialization we need to pass its module as the second // parameter. beforeEach(() => { - return MockBuilder() - .mock(TargetModule) - .keep(TOKEN_CLASS) - .keep(TOKEN_EXISTING) - .keep(TOKEN_FACTORY) - .keep(TOKEN_VALUE); + return MockBuilder([ + TOKEN_CLASS, + TOKEN_EXISTING, + TOKEN_FACTORY, + TOKEN_VALUE, + ], TargetModule); }); it('creates TOKEN_CLASS', () => { diff --git a/docs/articles/migrations.md b/docs/articles/migrations.md index 46f4657ff6..510453c5c4 100644 --- a/docs/articles/migrations.md +++ b/docs/articles/migrations.md @@ -10,6 +10,71 @@ 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 13 to 14 + +[`MockBuilder`](./api/MockBuilder.md) becomes stricter and starts to throw errors on wrong configuration. +If you call [`MockBuilder`](./api/MockBuilder.md) with 2 parameters and use the chain for dependencies: + +```ts +beforeEach(() => { + return MockBuilder(Declaration, ItsModule) + .keep(Dep1) + .mock(Dep2); +}); +``` + +[`MockBuilder`](./api/MockBuilder.md) throws an error +if `Dep1` or `Dep2` hasn't been imported or declared somewhere in `ItsModule` and its imports. + +You can change this to `console.warn` or disable it. +For that, please change the config of `ng-mocks` in `src/test.ts`, `src/setup-jest.ts` or `src/test-setup.ts`: + +```ts +ngMocks.config({ + onMockBuilderMissingDependency: 'warn', // or 'i-know-but-disable' +}); +``` + +If you need `Dep1` or `Dep2` even if they aren't imported in `ItsModule`, please add them in params of `MockBuilder`: + +```ts +beforeEach(() => { + return MockBuilder( + // Things to keep and export. + [Declaration, Dep1], + // Things to mock and export. + [ItsModule, Dep2], + ); +}); +``` + +Please note, that if you call [`MockBuilder`](./api/MockBuilder.md) with 0 or 1 parameters, all chained dependencies +are added to TestBed and exported by default now: + +```ts +// It doesn't throw, allows access to Declaration, Dep1, Dep2 and ItsModule in TestBed. +beforeEach(() => { + return MockBuilder(Declaration) + .mock(ItsModule) + .keep(Dep1) + .mock(Dep2); +}); + +// It doesn't throw, allows access to Declaration, Dep1, Dep2 and ItsModule in TestBed. +beforeEach(() => { + return MockBuilder() + .keep(Declaration) + .mock(ItsModule) + .keep(Dep1) + .mock(Dep2); +}); +``` + +## From 12 to 13 + +There are no special cases. +The update should be straight forward. + ## 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`. diff --git a/docs/articles/troubleshooting/declarations-of-2-modules.md b/docs/articles/troubleshooting/declarations-of-2-modules.md index c192ac421a..4591322b52 100644 --- a/docs/articles/troubleshooting/declarations-of-2-modules.md +++ b/docs/articles/troubleshooting/declarations-of-2-modules.md @@ -38,9 +38,8 @@ The only task now is to rewrite `beforeEach` to use [`MockBuilder`](../api/MockB ```ts beforeEach(() => { - return MockBuilder(ComponentToTest) - .keep(SharedModule) - .mock(ModuleWithServicesAndSharedModule); + return MockBuilder(MyComponent, ModuleWithServicesAndSharedModule) + .keep(SharedModule); }); ``` diff --git a/e2e/jest/jest.es2015ivy.js b/e2e/jest/jest.es2015ivy.js index 09f057f597..f75aadc0d8 100644 --- a/e2e/jest/jest.es2015ivy.js +++ b/e2e/jest/jest.es2015ivy.js @@ -1,7 +1,9 @@ module.exports = { preset: 'jest-preset-angular', setupFilesAfterEnv: ['/src/setup-jest.ts'], - testURL: 'http://localhost', + testEnvironmentOptions: { + url: 'http://localhost', + }, testPathIgnorePatterns: ['/src/test.ts'], globals: { 'ts-jest': { diff --git a/e2e/jest/jest.es5ivy.js b/e2e/jest/jest.es5ivy.js index e07fcbad42..31963380ab 100644 --- a/e2e/jest/jest.es5ivy.js +++ b/e2e/jest/jest.es5ivy.js @@ -1,7 +1,9 @@ module.exports = { preset: 'jest-preset-angular', setupFilesAfterEnv: ['/src/setup-jest.ts'], - testURL: 'http://localhost', + testEnvironmentOptions: { + url: 'http://localhost', + }, testPathIgnorePatterns: ['/src/test.ts'], globals: { 'ts-jest': { diff --git a/examples/MockComponent/test.spec.ts b/examples/MockComponent/test.spec.ts index 0e1c64bf96..3aa612d0ed 100644 --- a/examples/MockComponent/test.spec.ts +++ b/examples/MockComponent/test.spec.ts @@ -3,9 +3,11 @@ import { ContentChild, EventEmitter, Input, + NgModule, Output, TemplateRef, } from '@angular/core'; +import { CommonModule } from '@angular/common'; import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; @@ -33,18 +35,24 @@ class DependencyComponent { > `, }) -class TestedComponent { +class MyComponent { public value = ''; public trigger = (obj: any) => obj; } +@NgModule({ + imports: [CommonModule], + declarations: [MyComponent, DependencyComponent], +}) +class ItsModule {} + describe('MockComponent', () => { beforeEach(() => { - return MockBuilder(TestedComponent).mock(DependencyComponent); + return MockBuilder(MyComponent, ItsModule); }); it('sends the correct value to the child input', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -58,7 +66,7 @@ describe('MockComponent', () => { ).componentInstance; // Let's pretend that DependencyComponent has 'someInput' as - // an input. TestedComponent sets its value via + // an input. MyComponent sets its value via // `[someInput]="value"`. The input's value will be passed into // the mock component so we can assert on it. component.value = 'foo'; @@ -69,7 +77,7 @@ describe('MockComponent', () => { }); it('does something on an emit of the child component', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -80,7 +88,7 @@ describe('MockComponent', () => { const mockComponent = ngMocks.findInstance(DependencyComponent); // Again, let's pretend DependencyComponent has an output - // called 'someOutput'. TestedComponent listens on the output via + // called 'someOutput'. MyComponent listens on the output via // `(someOutput)="trigger($event)"`. // Let's install a spy and trigger the output. ngMocks.stubMember( diff --git a/examples/MockDirective-Attribute/test.spec.ts b/examples/MockDirective-Attribute/test.spec.ts index f3c7fba60c..aad67c1f96 100644 --- a/examples/MockDirective-Attribute/test.spec.ts +++ b/examples/MockDirective-Attribute/test.spec.ts @@ -3,6 +3,7 @@ import { Directive, EventEmitter, Input, + NgModule, Output, } from '@angular/core'; @@ -29,18 +30,24 @@ class DependencyDirective { > `, }) -class TestedComponent { +class MyComponent { public value = ''; public trigger = () => undefined; } +@NgModule({ + declarations: [MyComponent, DependencyDirective], +}) +class ItsModule {} + describe('MockDirective:Attribute', () => { beforeEach(() => { - return MockBuilder(TestedComponent).mock(DependencyDirective); + // DependencyDirective is a declaration in ItsModule. + return MockBuilder(MyComponent, ItsModule); }); it('sends the correct value to the input', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -54,7 +61,7 @@ describe('MockDirective:Attribute', () => { ); // Let's pretend DependencyDirective has 'someInput' - // as an input. TestedComponent sets its value via + // as an input. MyComponent sets its value via // `[someInput]="value"`. The input's value will be passed into // the mock directive so we can assert on it. component.value = 'foo'; @@ -65,7 +72,7 @@ describe('MockDirective:Attribute', () => { }); it('does something on an emit of the child directive', () => { - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // The same as @@ -79,7 +86,7 @@ describe('MockDirective:Attribute', () => { ); // Again, let's pretend DependencyDirective has an output called - // 'someOutput'. TestedComponent listens on the output via + // 'someOutput'. MyComponent listens on the output via // `(someOutput)="trigger()"`. // Let's install a spy and trigger the output. ngMocks.stubMember( diff --git a/examples/MockDirective-Structural/test.spec.ts b/examples/MockDirective-Structural/test.spec.ts index 723872889b..83160f7dec 100644 --- a/examples/MockDirective-Structural/test.spec.ts +++ b/examples/MockDirective-Structural/test.spec.ts @@ -33,7 +33,7 @@ class TargetComponent { @NgModule({ declarations: [TargetComponent, DependencyDirective], }) -class TargetModule {} +class ItsModule {} describe('MockDirective:Structural', () => { // IMPORTANT: by default structural directives are not rendered. @@ -41,7 +41,8 @@ describe('MockDirective:Structural', () => { // Usually a developer knows the context and can render it // manually with proper setup. beforeEach(() => { - return MockBuilder(TargetComponent, TargetModule).mock( + // DependencyDirective is a declaration in ItsModule. + return MockBuilder(TargetComponent, ItsModule).mock( DependencyDirective, { // render: true, // <-- a flag to render the directive by default diff --git a/examples/MockForms/test.spec.ts b/examples/MockForms/test.spec.ts index e0b7b9f6d4..9df4dd8862 100644 --- a/examples/MockForms/test.spec.ts +++ b/examples/MockForms/test.spec.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef } from '@angular/core'; +import { Component, forwardRef, NgModule } from '@angular/core'; import { ControlValueAccessor, FormsModule, @@ -33,18 +33,27 @@ class DependencyComponent implements ControlValueAccessor { selector: 'tested', template: ` `, }) -class TestedComponent { +class MyComponent { public value: any; } +@NgModule({ + imports: [FormsModule], + declarations: [MyComponent, DependencyComponent], +}) +class ItsModule {} + describe('MockForms', () => { // Helps to reset customizations after each test. MockInstance.scope(); beforeEach(() => { - return MockBuilder(TestedComponent) - .mock(DependencyComponent) - .keep(FormsModule); + // DependencyComponent is a declaration in ItsModule. + return ( + MockBuilder(MyComponent, ItsModule) + // FormsModule is an import in ItsModule. + .keep(FormsModule) + ); }); it('sends the correct value to the mock form component', async () => { @@ -61,7 +70,7 @@ describe('MockForms', () => { // the spy via MockInstance before the render. MockInstance(DependencyComponent, 'writeValue', writeValue); - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); // FormsModule needs fixture.whenStable() // right after MockRender to install all hooks. await fixture.whenStable(); diff --git a/examples/MockInstance/test.spec.ts b/examples/MockInstance/test.spec.ts index 569e0d34b1..17c78dc4e5 100644 --- a/examples/MockInstance/test.spec.ts +++ b/examples/MockInstance/test.spec.ts @@ -2,9 +2,11 @@ import { AfterViewInit, Component, Injector, + NgModule, ViewChild, } from '@angular/core'; import { Observable, Subject } from 'rxjs'; +import { CommonModule } from '@angular/common'; import { MockBuilder, MockInstance, MockRender } from 'ng-mocks'; @@ -39,11 +41,17 @@ class RealComponent implements AfterViewInit { } } +@NgModule({ + imports: [CommonModule], + declarations: [RealComponent, ChildComponent], +}) +class ItsModule {} + describe('MockInstance', () => { // A normal setup of the TestBed, TargetComponent will be replaced // with its mock object. // Do not forget to return the promise of MockBuilder. - beforeEach(() => MockBuilder(RealComponent).mock(ChildComponent)); + beforeEach(() => MockBuilder(RealComponent, ItsModule)); beforeEach(() => { // Because TargetComponent is replaced with its mock object, diff --git a/examples/MockModule/test.spec.ts b/examples/MockModule/test.spec.ts index 1e3650c159..3a13a10509 100644 --- a/examples/MockModule/test.spec.ts +++ b/examples/MockModule/test.spec.ts @@ -42,18 +42,25 @@ class DependencyModule {} > `, }) -class TestedComponent { +class MyComponent { public value = ''; public trigger = () => undefined; } +@NgModule({ + imports: [DependencyModule], + declarations: [MyComponent], +}) +class ItsModule {} + describe('MockModule', () => { beforeEach(() => { - return MockBuilder(TestedComponent).mock(DependencyModule); + // DependencyModule is an import of ItsModule. + return MockBuilder(MyComponent, ItsModule); }); - it('renders TestedComponent with its dependencies', () => { - const fixture = MockRender(TestedComponent); + it('renders MyComponent with its dependencies', () => { + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; expect(component).toBeTruthy(); diff --git a/examples/MockPipe/test.spec.ts b/examples/MockPipe/test.spec.ts index 7e811c6541..541af24bb6 100644 --- a/examples/MockPipe/test.spec.ts +++ b/examples/MockPipe/test.spec.ts @@ -23,7 +23,7 @@ class TargetComponent {} @NgModule({ declarations: [TargetComponent, DependencyPipe], }) -class TargetModule {} +class ItsModule {} // A fake transform function. const fakeTransform = (...args: string[]) => JSON.stringify(args); @@ -39,9 +39,10 @@ describe('MockPipe', () => { // const spy = jest.fn().mockImplementation(fakeTransform); beforeEach(() => { - return MockBuilder(TargetComponent, TargetModule).mock( - DependencyPipe, - spy, + return ( + MockBuilder(TargetComponent, ItsModule) + // DependencyPipe is a declaration in ItsModule + .mock(DependencyPipe, spy) ); }); diff --git a/examples/MockReactiveForms/test.spec.ts b/examples/MockReactiveForms/test.spec.ts index 06edff8b8f..346e2d7ea4 100644 --- a/examples/MockReactiveForms/test.spec.ts +++ b/examples/MockReactiveForms/test.spec.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef } from '@angular/core'; +import { Component, forwardRef, NgModule } from '@angular/core'; import { ControlValueAccessor, FormControl, @@ -34,18 +34,24 @@ class DependencyComponent implements ControlValueAccessor { selector: 'tested', template: ' ', }) -class TestedComponent { +class MyComponent { public readonly formControl = new FormControl(); } +@NgModule({ + imports: [ReactiveFormsModule], + declarations: [MyComponent, DependencyComponent], +}) +class ItsModule {} + describe('MockReactiveForms', () => { // Helps to reset MockInstance customizations after each test. MockInstance.scope(); beforeEach(() => { - return MockBuilder(TestedComponent) - .mock(DependencyComponent) - .keep(ReactiveFormsModule); + return MockBuilder(MyComponent, ItsModule).keep( + ReactiveFormsModule, + ); }); it('sends the correct value to the mock form component', () => { @@ -62,7 +68,7 @@ describe('MockReactiveForms', () => { // the spy via MockInstance before the render. MockInstance(DependencyComponent, 'writeValue', writeValue); - const fixture = MockRender(TestedComponent); + const fixture = MockRender(MyComponent); const component = fixture.point.componentInstance; // During initialization it should be called diff --git a/examples/MockRender/test.spec.ts b/examples/MockRender/test.spec.ts index 86c85d09f5..805d7cc033 100644 --- a/examples/MockRender/test.spec.ts +++ b/examples/MockRender/test.spec.ts @@ -37,7 +37,7 @@ class DependencyModule {} > `, }) -class TestedComponent { +class MyComponent { @Output() public readonly trigger = new EventEmitter(); @Input() public value1 = 'default1'; @Input() public value2 = 'default2'; @@ -45,7 +45,7 @@ class TestedComponent { describe('MockRender', () => { // Do not forget to return the promise of MockBuilder. - beforeEach(() => MockBuilder(TestedComponent, DependencyModule)); + beforeEach(() => MockBuilder(MyComponent, DependencyModule)); it('renders template', () => { const spy = @@ -93,7 +93,7 @@ describe('MockRender', () => { // Generates a template like: // . - const fixture = MockRender(TestedComponent, { + const fixture = MockRender(MyComponent, { trigger: spy, value1: 'something2', }); diff --git a/examples/TestRoute/test.spec.ts b/examples/TestRoute/test.spec.ts index ece7fa0dcb..68d97e79ab 100644 --- a/examples/TestRoute/test.spec.ts +++ b/examples/TestRoute/test.spec.ts @@ -60,8 +60,9 @@ class TargetModule {} describe('TestRoute:Route', () => { beforeEach(() => { - return MockBuilder(RouterModule, TargetModule).keep( - RouterTestingModule.withRoutes([]), + return MockBuilder( + [RouterModule, RouterTestingModule.withRoutes([])], + TargetModule, ); }); @@ -111,9 +112,14 @@ describe('TestRoute:Component', () => { // RouterTestingModule.withRoutes([]), yes yes, with empty routes // to have tools for testing. beforeEach(() => { - return MockBuilder(TargetComponent, TargetModule) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])); + return MockBuilder( + [ + TargetComponent, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + TargetModule, + ); }); it('navigates between pages', fakeAsync(() => { @@ -127,8 +133,8 @@ describe('TestRoute:Component', () => { tick(); // is needed for rendering of the current route. } - // By default our routes do not have a component. - // Therefore non of them should be rendered. + // By default, our routes do not have a component. + // Therefore, none of them should be rendered. expect(location.path()).toEqual('/'); expect(() => ngMocks.find(Target1Component)).toThrow(); expect(() => ngMocks.find(Target2Component)).toThrow(); diff --git a/examples/TestRoutingGuard/test.spec.ts b/examples/TestRoutingGuard/test.spec.ts index 5cb48f2d03..bf9101c3bc 100644 --- a/examples/TestRoutingGuard/test.spec.ts +++ b/examples/TestRoutingGuard/test.spec.ts @@ -115,10 +115,10 @@ describe('TestRoutingGuard', () => { // to have tools for testing. And the last thing is to exclude // `NG_MOCKS_GUARDS` to remove all other guards. beforeEach(() => { - return MockBuilder(LoginGuard, TargetModule) - .exclude(NG_MOCKS_GUARDS) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])); + return MockBuilder( + [LoginGuard, RouterModule, RouterTestingModule.withRoutes([])], + TargetModule, + ).exclude(NG_MOCKS_GUARDS); }); // It is important to run routing tests in fakeAsync. diff --git a/examples/TestRoutingResolver/test.spec.ts b/examples/TestRoutingResolver/test.spec.ts index 7033f5ce54..edd3dfce74 100644 --- a/examples/TestRoutingResolver/test.spec.ts +++ b/examples/TestRoutingResolver/test.spec.ts @@ -88,9 +88,14 @@ describe('TestRoutingResolver', () => { // add RouterTestingModule.withRoutes([]), yes yes, with empty // routes to have tools for testing. beforeEach(() => { - return MockBuilder(DataResolver, TargetModule) - .keep(RouterModule) - .keep(RouterTestingModule.withRoutes([])); + return MockBuilder( + [ + DataResolver, + RouterModule, + RouterTestingModule.withRoutes([]), + ], + TargetModule, + ); }); // It is important to run routing tests in fakeAsync. diff --git a/examples/TestToken/test.spec.ts b/examples/TestToken/test.spec.ts index 709052d98b..c58ceccec6 100644 --- a/examples/TestToken/test.spec.ts +++ b/examples/TestToken/test.spec.ts @@ -52,12 +52,10 @@ describe('TestToken', () => { // initialization we need to pass its module as the second // parameter. beforeEach(() => { - return MockBuilder() - .mock(TargetModule) - .keep(TOKEN_CLASS) - .keep(TOKEN_EXISTING) - .keep(TOKEN_FACTORY) - .keep(TOKEN_VALUE); + return MockBuilder( + [TOKEN_CLASS, TOKEN_EXISTING, TOKEN_FACTORY, TOKEN_VALUE], + TargetModule, + ); }); it('creates TOKEN_CLASS', () => { diff --git a/examples/ngMocksFaster/test.spec.ts b/examples/ngMocksFaster/test.spec.ts index 283767ef94..bd2dd63772 100644 --- a/examples/ngMocksFaster/test.spec.ts +++ b/examples/ngMocksFaster/test.spec.ts @@ -1,4 +1,4 @@ -import { Component, Injectable } from '@angular/core'; +import { Component, Injectable, NgModule } from '@angular/core'; import { MockBuilder, @@ -26,15 +26,20 @@ class TargetComponent { public constructor(public readonly service: TargetService) {} } +@NgModule({ + declarations: [TargetComponent], + providers: [TargetService], +}) +class ItsModule {} + describe('examples:performance', () => { describe('beforeEach:mock-instance', () => { ngMocks.faster(); // <-- add it before // A normal setup of the TestBed, TargetService will be replaced // with its mock copy. - beforeEach(() => { - return MockBuilder(TargetComponent).mock(TargetService); - }); + // TargetService is a provider in ItsModule. + beforeEach(() => MockBuilder(TargetComponent, ItsModule)); // Configuring behavior of the mock TargetService. beforeAll(() => { @@ -108,7 +113,11 @@ describe('examples:performance', () => { // A normal setup of the TestBed, TargetService will be replaced // with its mock copy. beforeEach(() => { - return MockBuilder(TargetComponent).mock(TargetService, mock); + return ( + MockBuilder(TargetComponent, ItsModule) + // TargetService is a provider in ItsModule. + .mock(TargetService, mock) + ); }); it('test:1', () => { diff --git a/libs/ng-mocks/src/lib/common/core.config.ts b/libs/ng-mocks/src/lib/common/core.config.ts index 828a6d50fe..2ddcafa0a3 100644 --- a/libs/ng-mocks/src/lib/common/core.config.ts +++ b/libs/ng-mocks/src/lib/common/core.config.ts @@ -30,6 +30,7 @@ export default { 'InjectionToken LocaleId', // LOCALE_ID 'InjectionToken SCHEDULER_TOKEN', // SCHEDULER ], + onMockBuilderMissingDependency: 'throw', onMockInstanceRestoreNeed: 'warn', onTestBedFlushNeed: 'warn', }; diff --git a/libs/ng-mocks/src/lib/common/core.reflect.provided-in.ts b/libs/ng-mocks/src/lib/common/core.reflect.provided-in.ts index 116cdb99e9..3d2aaa0be5 100644 --- a/libs/ng-mocks/src/lib/common/core.reflect.provided-in.ts +++ b/libs/ng-mocks/src/lib/common/core.reflect.provided-in.ts @@ -1,5 +1,9 @@ import { AnyType } from './core.types'; export default (declaration: any): undefined | AnyType | string => { - return declaration?.ɵprov?.providedIn ?? declaration?.ngInjectableDef?.providedIn; + if (!declaration || (typeof declaration !== 'object' && typeof declaration !== 'function')) { + return undefined; + } + + return declaration.ɵprov?.providedIn ?? declaration.ngInjectableDef?.providedIn; }; diff --git a/libs/ng-mocks/src/lib/common/core.tokens.ts b/libs/ng-mocks/src/lib/common/core.tokens.ts index 710cdc1938..9e830bca5b 100644 --- a/libs/ng-mocks/src/lib/common/core.tokens.ts +++ b/libs/ng-mocks/src/lib/common/core.tokens.ts @@ -13,6 +13,7 @@ import { AnyType } from './core.types'; * ``` */ export const NG_MOCKS = new InjectionToken>('NG_MOCKS'); +(NG_MOCKS as any).__ngMocksSkip = true; /** * NG_MOCKS_TOUCHES token is a set of all touched declarations during mock process. @@ -24,6 +25,7 @@ export const NG_MOCKS = new InjectionToken>('NG_MOCKS'); * ``` */ export const NG_MOCKS_TOUCHES = new InjectionToken>('NG_MOCKS_TOUCHES'); +(NG_MOCKS_TOUCHES as any).__ngMocksSkip = true; /** * NG_MOCKS_OVERRIDES token contains overrides for: @@ -38,6 +40,7 @@ export const NG_MOCKS_TOUCHES = new InjectionToken>('NG_MOCKS_TOUCHES') * @internal */ export const NG_MOCKS_OVERRIDES = new InjectionToken, MetadataOverride>>('NG_MOCKS_OVERRIDES'); +(NG_MOCKS_OVERRIDES as any).__ngMocksSkip = true; /** * NG_MOCKS_GUARDS token influences on provided guards in MockBuilder. @@ -47,6 +50,7 @@ export const NG_MOCKS_OVERRIDES = new InjectionToken, MetadataO * @see https://ng-mocks.sudo.eu/guides/routing-guard */ export const NG_MOCKS_GUARDS = new InjectionToken('NG_MOCKS_GUARDS'); +(NG_MOCKS_GUARDS as any).__ngMocksSkip = true; /** * NG_MOCKS_INTERCEPTORS token influences on provided interceptors in MockBuilder. @@ -56,6 +60,7 @@ export const NG_MOCKS_GUARDS = new InjectionToken('NG_MOCKS_GUARDS'); * @see https://ng-mocks.sudo.eu/guides/http-interceptor */ export const NG_MOCKS_INTERCEPTORS = new InjectionToken('NG_MOCKS_INTERCEPTORS'); +(NG_MOCKS_INTERCEPTORS as any).__ngMocksSkip = true; /** * NG_MOCKS_ROOT_PROVIDERS token influences on root providers in MockBuilder, @@ -65,3 +70,4 @@ export const NG_MOCKS_INTERCEPTORS = new InjectionToken('NG_MOCKS_INTERCEP * @see https://ng-mocks.sudo.eu/api/MockBuilder#ng_mocks_root_providers-token */ export const NG_MOCKS_ROOT_PROVIDERS = new InjectionToken('NG_MOCKS_ROOT_PROVIDERS'); +(NG_MOCKS_ROOT_PROVIDERS as any).__ngMocksSkip = true; diff --git a/libs/ng-mocks/src/lib/common/core.types.ts b/libs/ng-mocks/src/lib/common/core.types.ts index 57b0edbf01..378a1a7dbf 100644 --- a/libs/ng-mocks/src/lib/common/core.types.ts +++ b/libs/ng-mocks/src/lib/common/core.types.ts @@ -36,7 +36,7 @@ export type AnyType = Type | AbstractType; * * @internal */ -export type AnyDeclaration = AnyType | InjectionToken; +export type AnyDeclaration = AnyType | InjectionToken | string; /** * DebugNodeSelector describes supported types of selectors diff --git a/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts b/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts index 597d67faf0..d720e18d03 100644 --- a/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts +++ b/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts @@ -48,6 +48,8 @@ ngMocksUniverse.global = new Map(); ngMocksUniverse.touches = new Set(); ngMocksUniverse.global.set('flags', { + // @deprecated and will be changed in A13 to 'throw' + onMockBuilderMissingDependency: coreConfig.onMockBuilderMissingDependency, // @deprecated and will be changed in A13 to 'throw' onMockInstanceRestoreNeed: coreConfig.onMockInstanceRestoreNeed, // @deprecated and will be changed in A13 to 'throw' diff --git a/libs/ng-mocks/src/lib/mock-builder/mock-builder.performance.spec.ts b/libs/ng-mocks/src/lib/mock-builder/mock-builder.performance.spec.ts index 675c129684..2e8a17f3df 100644 --- a/libs/ng-mocks/src/lib/mock-builder/mock-builder.performance.spec.ts +++ b/libs/ng-mocks/src/lib/mock-builder/mock-builder.performance.spec.ts @@ -81,7 +81,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.imports?.[0]).not.toBe(ngModule2.imports?.[0]); }); - it('fails on a missed beforeCC', () => { + it('fails on a missing beforeCC', () => { const beforeCC1 = () => undefined; const beforeCC2 = () => undefined; @@ -109,7 +109,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.imports?.[0]).not.toBe(ngModule2.imports?.[0]); }); - it('fails on a missed keepDef', () => { + it('fails on a missing keepDef', () => { const ngModule1 = MockBuilder().keep(Target1Module).build(); const ngModule2 = MockBuilder().keep(Target2Module).build(); @@ -137,7 +137,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.imports?.[0]).not.toBe(ngModule2.imports?.[0]); }); - it('fails on a missed replaceDef', () => { + it('fails on a missing replaceDef', () => { const ngModule1 = MockBuilder() .keep(Target1Module) .replace(Target1Component, Target2Component) @@ -171,7 +171,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.imports?.[0]).not.toBe(ngModule2.imports?.[0]); }); - it('fails on a missed excludeDef', () => { + it('fails on a missing excludeDef', () => { const ngModule1 = MockBuilder() .keep(Target1Module) .exclude(Target1Component) @@ -202,7 +202,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.imports?.[0]).not.toBe(ngModule2.imports?.[0]); }); - it('fails on a missed mockDef', () => { + it('fails on a missing mockDef', () => { const ngModule1 = MockBuilder().mock(Target1Module).build(); const ngModule2 = MockBuilder().mock(Target2Module).build(); @@ -223,7 +223,7 @@ describe('MockBuilderPerformance', () => { ngModule2.providers?.[0], ); }); - it('fails on a missed providerDef', () => { + it('fails on a missing providerDef', () => { const ngModule1 = MockBuilder().provide(Target1Service).build(); const ngModule2 = MockBuilder().provide(Target2Service).build(); @@ -441,7 +441,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.imports?.[0]).not.toBe(ngModule2.imports?.[0]); }); - it('fails on a missed defProviders', () => { + it('fails on a missing defProviders', () => { const ngModule1 = MockBuilder() .keep(Target2Module) .keep({ @@ -549,7 +549,7 @@ describe('MockBuilderPerformance', () => { ngModule2.providers?.[0], ); }); - it('fails on a missed defValue', () => { + it('fails on a missing defValue', () => { const ngModule1 = MockBuilder() .mock(Target1Module) .mock(Target1Service) @@ -588,20 +588,22 @@ describe('MockBuilderPerformance', () => { }); it('fails on a different size configDef', () => { const ngModule1 = MockBuilder().keep(Target1Module).build(); - const ngModule2 = MockBuilder().keep(Target1Module, {}).build(); + const ngModule2 = MockBuilder() + .keep(Target1Module, { dependency: true }) + .build(); expect(ngModule1.providers?.[0]).not.toBe( ngModule2.providers?.[0], ); }); - it('fails on a missed configDef', () => { + it('fails on a missing configDef', () => { const ngModule1 = MockBuilder() .keep(Target1Module) - .keep(Target2Module, {}) + .keep(Target2Module, { dependency: true }) .build(); const ngModule2 = MockBuilder() .keep(Target2Module) - .keep(Target1Module, {}) + .keep(Target1Module, { dependency: true }) .build(); expect(ngModule1.providers?.[0]).not.toBe( @@ -755,7 +757,7 @@ describe('MockBuilderPerformance', () => { expect(ngModule1.providers?.[0]).toBe(ngModule2.providers?.[0]); }); - it('fails on missed definition configDef', () => { + it('fails on missing definition configDef', () => { const ngModule1 = MockBuilder() .mock(Target1Module, { render: {} }) .build(); diff --git a/libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts b/libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts index 720f8a0f78..87cacf448e 100644 --- a/libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts +++ b/libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts @@ -22,7 +22,7 @@ import initUniverse from './promise/init-universe'; import parseMockArguments from './promise/parse-mock-arguments'; import parseProvider from './promise/parse-provider'; import { BuilderData } from './promise/types'; -import { IMockBuilder, IMockBuilderConfig, IMockBuilderResult } from './types'; +import { IMockBuilder, IMockBuilderConfig, IMockBuilderConfigAll, IMockBuilderResult } from './types'; const normaliseModule = ( module: any, @@ -55,7 +55,7 @@ export class MockBuilderPromise implements IMockBuilder { protected replaceDef: BuilderData['replaceDef'] = new Set(); protected stash: MockBuilderStash = new MockBuilderStash(); - public constructor() { + public constructor(protected configDefault: IMockBuilderConfigAll) { // istanbul ignore else if (typeof Symbol !== 'undefined') { (this as any)[Symbol.toStringTag] = 'Promise'; @@ -126,11 +126,7 @@ export class MockBuilderPromise implements IMockBuilder { this.defProviders.set(def, [...(existing || /* istanbul ignore next */ []), ...providers]); } - if (config) { - this.configDef.set(def, config); - } else { - this.configDef.delete(def); - } + this.setConfigDef(def, config); return this; } @@ -183,12 +179,7 @@ export class MockBuilderPromise implements IMockBuilder { this.wipe(source); this.replaceDef.add(source); this.defValue.set(source, destination); - - if (config) { - this.configDef.set(source, config); - } else { - this.configDef.delete(source); - } + this.setConfigDef(source, config); return this; } @@ -215,6 +206,7 @@ export class MockBuilderPromise implements IMockBuilder { private combineParams(): BuilderData { return { configDef: this.configDef, + configDefault: this.configDefault, defProviders: this.defProviders, defValue: this.defValue, excludeDef: this.excludeDef, @@ -226,11 +218,7 @@ export class MockBuilderPromise implements IMockBuilder { } private setConfigDef(def: any, config: any): void { - if (config) { - this.configDef.set(def, config); - } else { - this.configDef.delete(def); - } + this.configDef.set(def, config ?? this.configDefault); } private setDefValue(def: any, mock: any): void { diff --git a/libs/ng-mocks/src/lib/mock-builder/mock-builder.ts b/libs/ng-mocks/src/lib/mock-builder/mock-builder.ts index 7c6a76ed2c..2838cf454a 100644 --- a/libs/ng-mocks/src/lib/mock-builder/mock-builder.ts +++ b/libs/ng-mocks/src/lib/mock-builder/mock-builder.ts @@ -16,8 +16,12 @@ export type MockBuilderParam = string | AnyDeclaration | NgModuleWithProvid export function MockBuilder( keepDeclaration?: MockBuilderParam | MockBuilderParam[] | null | undefined, itsModuleToMock?: MockBuilderParam | MockBuilderParam[] | null | undefined, -): IMockBuilder { - const instance = new MockBuilderPerformance(); +): IMockBuilder; + +export function MockBuilder(...args: Array): IMockBuilder { + const [keepDeclaration, itsModuleToMock] = args; + + const instance = new MockBuilderPerformance(args.length < 2 ? { export: true } : { dependency: true }); if (keepDeclaration) { for (const declaration of flatten(keepDeclaration)) { diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts b/libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts index 938c5ea037..f2ad7d0e2d 100644 --- a/libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts +++ b/libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts @@ -3,6 +3,9 @@ import funcGetProvider from '../../common/func.get-provider'; import { isNgDef } from '../../common/func.is-ng-def'; import ngMocksUniverse from '../../common/ng-mocks-universe'; import markProviders from '../../mock-module/mark-providers'; +import funcGetName from '../../common/func.get-name'; +import { AnyDeclaration } from '../../common/core.types'; +import coreReflectProvidedIn from '../../common/core.reflect.provided-in'; import initModule from './init-module'; import { BuilderData, NgMeta } from './types'; @@ -41,19 +44,52 @@ const handleDef = ({ imports, declarations, providers }: NgMeta, def: any, defPr } }; -export default ({ configDef, keepDef, mockDef, replaceDef }: BuilderData, defProviders: Map): NgMeta => { +export default ( + { configDef, configDefault, keepDef, mockDef, replaceDef }: BuilderData, + defProviders: Map, +): NgMeta => { const meta: NgMeta = { imports: [], declarations: [], providers: [] }; + const forgotten: AnyDeclaration[] = []; + // Adding suitable leftovers. for (const def of [...mapValues(mockDef), ...mapValues(keepDef), ...mapValues(replaceDef)]) { const configInstance = ngMocksUniverse.configInstance.get(def); const config = configDef.get(def); - if (!config?.dependency && config?.export && !configInstance?.exported && (isNgDef(def, 'i') || !isNgDef(def))) { + if (!config.dependency && config.export && !configInstance?.exported && (isNgDef(def, 'i') || !isNgDef(def))) { handleDef(meta, def, defProviders); markProviders([def]); - } else if (!ngMocksUniverse.touches.has(def) && !config?.dependency) { + } else if (!ngMocksUniverse.touches.has(def) && !config.dependency) { handleDef(meta, def, defProviders); + } else if ( + config.dependency && + configDefault.dependency && + coreReflectProvidedIn(def) !== 'root' && + (typeof def !== 'object' || !(def as any).__ngMocksSkip) + ) { + forgotten.push(def); + } + } + + // Checking missing dependencies + const globalFlags = ngMocksUniverse.global.get('flags'); + for (const def of forgotten) { + if (ngMocksUniverse.touches.has(def)) { + continue; + } + + const errorMessage = [ + `MockBuilder has found a missing dependency: ${funcGetName(def)}.`, + 'It means no module provides it.', + 'Please, use the "export" flag if you want to add it explicitly.', + 'https://ng-mocks.sudo.eu/api/MockBuilder#export-flag', + ].join(' '); + + if (globalFlags.onMockBuilderMissingDependency === 'warn') { + console.warn(errorMessage); + } else if (globalFlags.onMockBuilderMissingDependency === 'throw') { + throw new Error(errorMessage); } } diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/try-mock-provider.ts b/libs/ng-mocks/src/lib/mock-builder/promise/try-mock-provider.ts index 5339d68f51..cd91f7c21a 100644 --- a/libs/ng-mocks/src/lib/mock-builder/promise/try-mock-provider.ts +++ b/libs/ng-mocks/src/lib/mock-builder/promise/try-mock-provider.ts @@ -15,7 +15,7 @@ const createInstance = (existing: any, params: any, config: IMockBuilderConfigMo export default (def: any, defValue: Map): void => { if (isNgDef(def, 'i') && defValue.has(def)) { - const config: IMockBuilderConfigMock = ngMocksUniverse.config.get(def) || {}; + const config: IMockBuilderConfigMock = ngMocksUniverse.config.get(def); const instance = defValue.get(def); ngMocksUniverse.builtProviders.set( def, diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/types.ts b/libs/ng-mocks/src/lib/mock-builder/promise/types.ts index ee92dc5948..515cb83828 100644 --- a/libs/ng-mocks/src/lib/mock-builder/promise/types.ts +++ b/libs/ng-mocks/src/lib/mock-builder/promise/types.ts @@ -4,9 +4,11 @@ import { InjectionToken, Provider } from '@angular/core'; import { Type } from '../../common/core.types'; import { NgModuleWithProviders } from '../../common/func.is-ng-module-def-with-providers'; +import { IMockBuilderConfigAll } from '../types'; export type BuilderData = { configDef: Map | InjectionToken | string, any>; + configDefault: IMockBuilderConfigAll; defProviders: Map | InjectionToken | string, Provider[]>; defValue: Map | InjectionToken | string, any>; excludeDef: Set | InjectionToken | string>; diff --git a/libs/ng-mocks/src/lib/mock-builder/types.ts b/libs/ng-mocks/src/lib/mock-builder/types.ts index e99c714784..0925a9a228 100644 --- a/libs/ng-mocks/src/lib/mock-builder/types.ts +++ b/libs/ng-mocks/src/lib/mock-builder/types.ts @@ -19,7 +19,7 @@ export interface IMockBuilderConfigAll { /** * @see https://ng-mocks.sudo.eu/api/MockBuilder#dependency-flag */ - dependency?: boolean; // + dependency?: boolean; /** * @see https://ng-mocks.sudo.eu/api/MockBuilder#export-flag diff --git a/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instance.ts b/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instance.ts index e4804b4341..2a74936277 100644 --- a/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instance.ts +++ b/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instance.ts @@ -15,7 +15,7 @@ const defaultNotFoundValue = {}; // simulating Symbol export default (...args: any[]): T => { const [el, sel, notFoundValue] = funcParseFindArgs(args, funcIsValidFindInstanceSelector, defaultNotFoundValue); - if (typeof sel !== 'function' && !isNgDef(sel, 't')) { + if (typeof sel !== 'function' && !isNgDef(sel, 't') && typeof sel !== 'string') { throw new Error('Only classes or tokens are accepted'); } diff --git a/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instances.ts b/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instances.ts index df7d740b2c..05495c3e8a 100644 --- a/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instances.ts +++ b/libs/ng-mocks/src/lib/mock-helper/find-instance/mock-helper.find-instances.ts @@ -12,7 +12,7 @@ import funcIsValidFindInstanceSelector from './func.is-valid-find-instance-selec export default (...args: any[]): T[] => { const [el, sel] = funcParseFindArgs(args, funcIsValidFindInstanceSelector); - if (typeof sel !== 'function' && !isNgDef(sel, 't')) { + if (typeof sel !== 'function' && !isNgDef(sel, 't') && typeof sel !== 'string') { throw new Error('Only classes or tokens are accepted'); } diff --git a/libs/ng-mocks/src/lib/mock-helper/func.get-from-node.ts b/libs/ng-mocks/src/lib/mock-helper/func.get-from-node.ts index 295e0c907d..0b2d475457 100644 --- a/libs/ng-mocks/src/lib/mock-helper/func.get-from-node.ts +++ b/libs/ng-mocks/src/lib/mock-helper/func.get-from-node.ts @@ -30,7 +30,7 @@ export interface Node { export default (result: T[], node: DebugNode & Node, proto: AnyDeclaration): T[] => { funcGetFromNodeInjector(result, node, proto); - if (!isNgDef(proto, 't')) { + if (!isNgDef(proto, 't') && typeof proto !== 'string') { funcGetFromNodeStandard(result, node, proto); funcGetFromNodeIvy(result, node, proto); } diff --git a/libs/ng-mocks/src/lib/mock-helper/mock-helper.faster.ts b/libs/ng-mocks/src/lib/mock-helper/mock-helper.faster.ts index f604027580..f1722b18eb 100644 --- a/libs/ng-mocks/src/lib/mock-helper/mock-helper.faster.ts +++ b/libs/ng-mocks/src/lib/mock-helper/mock-helper.faster.ts @@ -13,6 +13,8 @@ const resetFixtures = (id: never) => { for (let i = activeFixtures.length - 1; i >= 0; i -= 1) { if (!activeFixtures[i].ngMocksStackId || activeFixtures[i].ngMocksStackId === id) { activeFixtures[i].ngMocksStackId = undefined; + activeFixtures[i].destroy(); + activeFixtures.splice(i, 1); } else { active += 1; } diff --git a/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts b/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts index 854625c737..b3973e64a4 100644 --- a/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts +++ b/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts @@ -39,17 +39,20 @@ import mockHelperRender from './render/mock-helper.render'; import mockHelperFindTemplateRef from './template-ref/mock-helper.find-template-ref'; import mockHelperFindTemplateRefs from './template-ref/mock-helper.find-template-refs'; +const flagNames = ['onMockBuilderMissingDependency', 'onMockInstanceRestoreNeed', 'onTestBedFlushNeed'] as const; + export default { autoSpy: mockHelperAutoSpy, change: mockHelperChange, click: mockHelperClick, config: (config: { mockRenderCacheSize?: number | null; + onMockBuilderMissingDependency?: 'throw' | 'warn' | 'i-know-but-disable' | null; onMockInstanceRestoreNeed?: 'throw' | 'warn' | 'i-know-but-disable' | null; onTestBedFlushNeed?: 'throw' | 'warn' | 'i-know-but-disable' | null; }) => { const flags = ngMocksUniverse.global.get('flags'); - for (const flag of ['onTestBedFlushNeed', 'onMockInstanceRestoreNeed'] as const) { + for (const flag of flagNames) { if (config[flag] === null) { flags[flag] = coreConfig[flag]; } else if (config[flag] !== undefined) { diff --git a/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts b/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts index 7b2dd38eec..7c2fc01705 100644 --- a/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts +++ b/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts @@ -1035,6 +1035,7 @@ export const ngMocks: { */ config(config: { mockRenderCacheSize?: number | null; + onMockBuilderMissingDependency?: 'throw' | 'warn' | 'i-know-but-disable' | null; onMockInstanceRestoreNeed?: 'throw' | 'warn' | 'i-know-but-disable' | null; onTestBedFlushNeed?: 'throw' | 'warn' | 'i-know-but-disable' | null; }): void; diff --git a/tests-e2e/src/issue-312/import-features.spec.ts b/tests-e2e/src/issue-312/import-features.spec.ts index 3ebffcf101..9001199247 100644 --- a/tests-e2e/src/issue-312/import-features.spec.ts +++ b/tests-e2e/src/issue-312/import-features.spec.ts @@ -89,15 +89,20 @@ class MyModule {} describe('issue-312:import-features', () => { beforeEach(() => - MockBuilder(MyComponent, MyModule) - .keep(FormsModule) - .keep(ReactiveFormsModule) - .keep(StoreModule.forRoot({})) - .keep(EffectsModule.forRoot()) + MockBuilder( + [ + MyComponent, + StoreModule.forRoot({}), + EffectsModule.forRoot(), + StoreModule.forFeature('f1', createReducer(undefined)), + StoreModule.forFeature('f2', createReducer(undefined)), + FormsModule, + ReactiveFormsModule, + ], + MyModule, + ) .keep(StoreFeatureModule) - .keep(EffectsFeatureModule) - .keep(StoreModule.forFeature('f1', createReducer(undefined))) - .keep(StoreModule.forFeature('f2', createReducer(undefined))), + .keep(EffectsFeatureModule), ); it('providers modules correctly', () => { diff --git a/tests-e2e/src/issue-312/only-feature.spec.ts b/tests-e2e/src/issue-312/only-feature.spec.ts index 285f32dd7a..c6699d86f1 100644 --- a/tests-e2e/src/issue-312/only-feature.spec.ts +++ b/tests-e2e/src/issue-312/only-feature.spec.ts @@ -111,9 +111,14 @@ describe('issue-312:only-feature', () => { describe('builder', () => { beforeEach(() => - MockBuilder(MyComponent, MyModule) - .keep(StoreModule.forRoot({})) - .keep(EffectsModule.forRoot()) + MockBuilder( + [ + MyComponent, + StoreModule.forRoot({}), + EffectsModule.forRoot(), + ], + MyModule, + ) .keep(StoreFeatureModule) .keep(EffectsFeatureModule), ); diff --git a/tests-e2e/src/issue-488/faster.spec.ts b/tests-e2e/src/issue-488/faster.spec.ts index b7b1509173..deb9dfb29b 100644 --- a/tests-e2e/src/issue-488/faster.spec.ts +++ b/tests-e2e/src/issue-488/faster.spec.ts @@ -1,6 +1,5 @@ import { CommonModule } from '@angular/common'; import { Component, NgModule, OnInit } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { createAction, createFeatureSelector, @@ -66,9 +65,10 @@ describe('issue-488', () => { ngMocks.faster(); beforeAll(() => - MockBuilder(MyComponent, MyModule) - .keep(StoreModule.forRoot({})) - .keep(StoreFeatureModule), + MockBuilder( + [MyComponent, StoreModule.forRoot({})], + MyModule, + ).keep(StoreFeatureModule), ); describe('faster multi render', () => { @@ -77,7 +77,7 @@ describe('issue-488', () => { it('first test has brand new render', () => { expect(ngMocks.formatText(fixture)).toEqual('1'); - TestBed.get(Store).dispatch(increaseValue()); + ngMocks.findInstance(Store).dispatch(increaseValue()); fixture.detectChanges(); expect(ngMocks.formatText(fixture)).toEqual('2'); @@ -89,7 +89,7 @@ describe('issue-488', () => { it('second test has brand new render', () => { expect(ngMocks.formatText(fixture)).toEqual('1'); - TestBed.get(Store).dispatch(increaseValue()); + ngMocks.findInstance(Store).dispatch(increaseValue()); fixture.detectChanges(); expect(ngMocks.formatText(fixture)).toEqual('2'); @@ -105,7 +105,7 @@ describe('issue-488', () => { it('first test has render of 1', () => { expect(ngMocks.formatText(fixture)).toEqual('1'); - TestBed.get(Store).dispatch(increaseValue()); + ngMocks.findInstance(Store).dispatch(increaseValue()); fixture.detectChanges(); expect(ngMocks.formatText(fixture)).toEqual('2'); @@ -117,7 +117,7 @@ describe('issue-488', () => { it('second test continues the prev state', () => { expect(ngMocks.formatText(fixture)).toEqual('0'); - TestBed.get(Store).dispatch(increaseValue()); + ngMocks.findInstance(Store).dispatch(increaseValue()); fixture.detectChanges(); expect(ngMocks.formatText(fixture)).toEqual('1'); diff --git a/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts b/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts index 9402c18b29..0ea56bd0d5 100644 --- a/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts +++ b/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts @@ -11,6 +11,7 @@ import { NG_MOCKS_ROOT_PROVIDERS, } from 'ng-mocks'; +// @TODO remove with A5 support const injectableTarget1ServiceArgs = [ { providedIn: 'root', @@ -36,6 +37,7 @@ class Target1Component { }) class Target1Module {} +// @TODO remove with A5 support const injectableTarget2ServiceArgs = [ { providedIn: 'root', diff --git a/tests/auto-spy/test.spec.ts b/tests/auto-spy/test.spec.ts index 4776dee5bc..9c1bbf5c87 100644 --- a/tests/auto-spy/test.spec.ts +++ b/tests/auto-spy/test.spec.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, ngMocks } from 'ng-mocks'; @@ -17,7 +16,7 @@ describe('auto-spy', () => { beforeEach(() => MockBuilder().mock(TargetService)); it('returns mock', () => { - const service: TargetService = TestBed.get(TargetService); + const service = ngMocks.findInstance(TargetService); expect(service.echo).not.toHaveBeenCalled(); service.echo(); @@ -32,7 +31,7 @@ describe('auto-spy', () => { beforeEach(() => MockBuilder().mock(TargetService)); it('returns mock', () => { - const service: TargetService = TestBed.get(TargetService); + const service = ngMocks.findInstance(TargetService); expect(() => expect(service.echo).not.toHaveBeenCalled(), ).toThrow(); @@ -54,7 +53,7 @@ describe('auto-spy', () => { beforeEach(() => MockBuilder().mock(TargetService)); it('returns mock', () => { - const service: TargetService = TestBed.get(TargetService); + const service = ngMocks.findInstance(TargetService); expect(called).toBeUndefined(); service.echo(); diff --git a/tests/flex-exact-mocks/test.spec.ts b/tests/flex-exact-mocks/test.spec.ts index 0fe3e53678..649b9e9fec 100644 --- a/tests/flex-exact-mocks/test.spec.ts +++ b/tests/flex-exact-mocks/test.spec.ts @@ -1,10 +1,9 @@ import { Injectable, InjectionToken } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { Observable, Subject } from 'rxjs'; -import { MockBuilder } from 'ng-mocks'; +import { MockBuilder, ngMocks } from 'ng-mocks'; -const TOKEN = new InjectionToken('TOKEN'); +const TOKEN = new InjectionToken('TOKEN'); @Injectable() class TargetService { @@ -31,19 +30,19 @@ describe('flex-exact-mocks:no-precise', () => { }); it('extends a mock service', () => { - // By default a service is extended with its mock copy. - const service: TargetService = TestBed.get(TargetService); + // By default, a service is extended with its mock copy. + const service = ngMocks.findInstance(TargetService); expect(service.o1$).toBeDefined(); expect(service.o1$).toBe(mockService.o1$); expect(service.echo).toBeDefined(); expect(service.echo()).toBeUndefined(); // Tokens should stay as they are. - const token: typeof mockToken = TestBed.get(TOKEN); + const token: typeof mockToken = ngMocks.findInstance(TOKEN); expect(token).toBe(mockToken); // strings should stay as they are. - const str: typeof mockStr = TestBed.get('token'); + const str: typeof mockStr = ngMocks.findInstance('token'); expect(str).toBe(mockStr); }); }); @@ -65,15 +64,15 @@ describe('flex-exact-mocks:precise', () => { it('extends a mock service', () => { // The instance should be the passed mock copy due to the flag. - const service: TargetService = TestBed.get(TargetService); + const service = ngMocks.findInstance(TargetService); expect(service).toBe(mock as any); // The flag does not affect tokens. - const token: typeof mockToken = TestBed.get(TOKEN); + const token: typeof mockToken = ngMocks.findInstance(TOKEN); expect(token).toBe(mockToken); // The flag does not affect strings. - const str: typeof mockStr = TestBed.get('token'); + const str: typeof mockStr = ngMocks.findInstance('token'); expect(str).toBe(mockStr); }); }); diff --git a/tests/interceptor-kept-mocked/test.spec.ts b/tests/interceptor-kept-mocked/test.spec.ts index 513682c372..631433a801 100644 --- a/tests/interceptor-kept-mocked/test.spec.ts +++ b/tests/interceptor-kept-mocked/test.spec.ts @@ -12,10 +12,14 @@ import { HttpTestingController, } from '@angular/common/http/testing'; import { Injectable, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { Observable } from 'rxjs'; -import { MockBuilder, MockInstance, MockReset } from 'ng-mocks'; +import { + MockBuilder, + MockInstance, + MockReset, + ngMocks, +} from 'ng-mocks'; @Injectable() class Target1Interceptor implements HttpInterceptor { @@ -91,10 +95,8 @@ describe('interceptor-kept-mock', () => { afterAll(MockReset); it('triggers interceptor', () => { - const client: HttpClient = TestBed.get(HttpClient); - const httpMock: HttpTestingController = TestBed.get( - HttpTestingController, - ); + const client = ngMocks.findInstance(HttpClient); + const httpMock = ngMocks.findInstance(HttpTestingController); // Let's do a simply request. client.get('/target').subscribe(); diff --git a/tests/internal-only-nested/test.spec.ts b/tests/internal-only-nested/test.spec.ts index 3d40fae7fb..ff483460a9 100644 --- a/tests/internal-only-nested/test.spec.ts +++ b/tests/internal-only-nested/test.spec.ts @@ -21,7 +21,7 @@ describe('InternalOnlyNested:mock', () => { .mock(InternalComponent, { export: true }), ); - // The expectation is to see that InternalComponent was exported to the level of the TestingModule + // The expectation is to see that InternalComponent was exported to the level of the MyModule // and can be accessed in the test even it was deeply nested. it('should render', () => { const fixture = MockRender(InternalComponent); diff --git a/tests/issue-1256/test.spec.ts b/tests/issue-1256/test.spec.ts index 257216ae74..f46be00250 100644 --- a/tests/issue-1256/test.spec.ts +++ b/tests/issue-1256/test.spec.ts @@ -1,7 +1,7 @@ import { InjectionToken } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { MockInstance, MockProvider } from 'ng-mocks'; +import { MockInstance, MockProvider, ngMocks } from 'ng-mocks'; const myToken = new InjectionToken('MY_TOKEN'); @@ -18,12 +18,12 @@ describe('issue-1256', () => { it('changes value #1', () => { MockInstance(myToken, () => ({ test: 2 })); - const value: any = TestBed.get(myToken); + const value = ngMocks.findInstance(myToken); expect(value.test).toEqual(2); }); it('uses the default value', () => { - const value: any = TestBed.get(myToken); + const value = ngMocks.findInstance(myToken); expect(value.test).toEqual(1); }); }); diff --git a/tests/issue-151/test.spec.ts b/tests/issue-151/test.spec.ts index ef57e4e36a..475b68c0d3 100644 --- a/tests/issue-151/test.spec.ts +++ b/tests/issue-151/test.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Router, RouterModule } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { MockBuilder, MockRender } from 'ng-mocks'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; import { AppComponent } from './app/app.component'; import { AppModule } from './app/app.module'; @@ -14,16 +14,16 @@ describe('issue-151', () => { describe('mock AppRoutingModule', () => { beforeEach(() => TestBed.configureTestingModule( - MockBuilder(AppComponent, AppModule) - .keep(RouterModule) - .keep(RouterTestingModule) - .build(), + MockBuilder( + [AppComponent, RouterModule, RouterTestingModule], + AppModule, + ).build(), ), ); beforeEach(async () => { fixture = MockRender(AppComponent); - const router = TestBed.get(Router); + const router = ngMocks.findInstance(Router); if (fixture.ngZone) { fixture.ngZone.run(() => router.initialNavigation()); } diff --git a/tests/issue-162/test.spec.ts b/tests/issue-162/test.spec.ts index 15fff853d1..bac05e2f98 100644 --- a/tests/issue-162/test.spec.ts +++ b/tests/issue-162/test.spec.ts @@ -42,7 +42,7 @@ describe('issue-162', () => { it('verifies that EventManager was not replaced with a mock copy', () => { MockRender(AppComponent); TestBed.resetTestingModule(); - const target: EventManager = TestBed.get(EventManager); + const target = ngMocks.findInstance(EventManager); expect(target.addEventListener).toEqual(assertion.any(Function)); expect( (target.addEventListener as any).__ngMocks, @@ -53,7 +53,7 @@ describe('issue-162', () => { it('verifies that RendererFactory2 was not replaced with a mock copy', () => { MockRender(AppComponent); TestBed.resetTestingModule(); - const target: RendererFactory2 = TestBed.get(RendererFactory2); + const target = ngMocks.findInstance(RendererFactory2); expect(target.createRenderer).toEqual(assertion.any(Function)); expect((target.createRenderer as any).__ngMocks).toBeUndefined(); }); diff --git a/tests/issue-186/test.spec.ts b/tests/issue-186/test.spec.ts index 5247d6a5dc..37694b00a5 100644 --- a/tests/issue-186/test.spec.ts +++ b/tests/issue-186/test.spec.ts @@ -1,7 +1,7 @@ import { Injectable, NgModule } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { MockModule } from 'ng-mocks'; +import { MockModule, ngMocks } from 'ng-mocks'; @Injectable() class ExampleProvider { @@ -22,7 +22,7 @@ describe('issue-186:real', () => { imports: [ExampleModule], }); - exampleProvider = TestBed.get(ExampleProvider); + exampleProvider = ngMocks.findInstance(ExampleProvider); }); it('should not be able to pass state between tests (setup phase)', () => { @@ -47,7 +47,7 @@ describe('issue-186:mock', () => { imports: [MockModule(ExampleModule)], }); - exampleProvider = TestBed.get(ExampleProvider); + exampleProvider = ngMocks.findInstance(ExampleProvider); }); it('should not be able to pass state between tests (setup phase)', () => { diff --git a/tests/issue-197/abstract.spec.ts b/tests/issue-197/abstract.spec.ts index 589057a77c..46488427f2 100644 --- a/tests/issue-197/abstract.spec.ts +++ b/tests/issue-197/abstract.spec.ts @@ -1,7 +1,6 @@ -import { TestBed } from '@angular/core/testing'; import { DomSanitizer } from '@angular/platform-browser'; -import { MockBuilder } from 'ng-mocks'; +import { MockBuilder, ngMocks } from 'ng-mocks'; // @see https://github.com/ike18t/ng-mocks/issues/197 describe('issue-197:abstract', () => { @@ -13,7 +12,7 @@ describe('issue-197:abstract', () => { }); it('mocks abstract classes', () => { - const actual = TestBed.get(DomSanitizer); + const actual = ngMocks.findInstance(DomSanitizer); expect(actual).toBe(expected); }); }); diff --git a/tests/issue-197/with-providers.spec.ts b/tests/issue-197/with-providers.spec.ts index 9542c49766..bb9f69d5fe 100644 --- a/tests/issue-197/with-providers.spec.ts +++ b/tests/issue-197/with-providers.spec.ts @@ -70,8 +70,9 @@ describe('issue-197:with-providers:manually-injection', () => { // @see https://github.com/ike18t/ng-mocks/issues/197 describe('issue-197:with-providers:keep', () => { beforeEach(() => - MockBuilder(TargetComponent, TargetModule).keep( - DependencyModule.withProviders(), + MockBuilder( + [TargetComponent, DependencyModule.withProviders()], + TargetModule, ), ); @@ -87,9 +88,10 @@ describe('issue-197:with-providers:keep', () => { // @see https://github.com/ike18t/ng-mocks/issues/197 describe('issue-197:with-providers:mock', () => { beforeEach(() => - MockBuilder(TargetComponent, TargetModule).mock( + MockBuilder(TargetComponent, [ + TargetModule, DependencyModule.withProviders(), - ), + ]), ); it('creates component with provided dependencies', () => { diff --git a/tests/issue-222/application-module.spec.ts b/tests/issue-222/application-module.spec.ts index 9628792d6b..df79756d2f 100644 --- a/tests/issue-222/application-module.spec.ts +++ b/tests/issue-222/application-module.spec.ts @@ -1,8 +1,7 @@ import { ApplicationRef, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { BrowserModule } from '@angular/platform-browser'; -import { MockBuilder } from 'ng-mocks'; +import { MockBuilder, ngMocks } from 'ng-mocks'; @NgModule({ imports: [BrowserModule], @@ -14,7 +13,7 @@ describe('issue-222:application-module', () => { beforeEach(() => MockBuilder(null, TargetModule)); it('does not mock its guts', () => { - const service = TestBed.get(ApplicationRef); + const service = ngMocks.findInstance(ApplicationRef); expect(service.viewCount).toBeDefined(); }); }); diff --git a/tests/issue-222/injector-scope.spec.ts b/tests/issue-222/injector-scope.spec.ts index 833efe3300..8113144d51 100644 --- a/tests/issue-222/injector-scope.spec.ts +++ b/tests/issue-222/injector-scope.spec.ts @@ -10,6 +10,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { MockBuilder, MockRender } from 'ng-mocks'; +// @TODO remove with A5 support const injectableArgs = [ { providedIn: 'root', diff --git a/tests/issue-222/kept-root-injection.spec.ts b/tests/issue-222/kept-root-injection.spec.ts index 3f11fb51f2..236cc904d2 100644 --- a/tests/issue-222/kept-root-injection.spec.ts +++ b/tests/issue-222/kept-root-injection.spec.ts @@ -4,10 +4,14 @@ import { NgModule, VERSION, } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { MockBuilder, NG_MOCKS_ROOT_PROVIDERS } from 'ng-mocks'; +import { + MockBuilder, + NG_MOCKS_ROOT_PROVIDERS, + ngMocks, +} from 'ng-mocks'; +// @TODO remove with A5 support const injectableTargetServiceArgs = [ { providedIn: 'root', @@ -70,7 +74,7 @@ describe('issue-222:kept-root-injection', () => { ); it('does not mock kept dependency', () => { - const service: TargetService = TestBed.get(TargetService); + const service = ngMocks.findInstance(TargetService); expect(service.echo()).toBeDefined(); }); }); @@ -83,7 +87,7 @@ describe('issue-222:kept-root-injection', () => { ); it('does not mock kept dependency', () => { - const service: TargetService = TestBed.get(TargetService); + const service = ngMocks.findInstance(TargetService); expect(service.echo()).toBeUndefined(); }); }); diff --git a/tests/issue-2647/errors.spec.ts b/tests/issue-2647/errors.spec.ts new file mode 100644 index 0000000000..b96f6e442c --- /dev/null +++ b/tests/issue-2647/errors.spec.ts @@ -0,0 +1,434 @@ +import { + Component, + Directive, + Injectable, + NgModule, + Pipe, + PipeTransform, + VERSION, +} from '@angular/core'; + +import { MockBuilder } from 'ng-mocks'; + +// @TODO remove with A5 support +const injectableRootServiceArgs = [ + { + providedIn: 'root', + } as never, +]; + +@Injectable(...injectableRootServiceArgs) +class RootService {} + +@Injectable() +class MissingService {} + +@Pipe({ + name: 'missing', +}) +class MissingPipe implements PipeTransform { + constructor(public readonly missing: MissingService) {} + + transform(): string { + return this.constructor.name; + } +} + +@Directive({ + selector: 'missing', +}) +class MissingDirective { + constructor(public readonly missing: MissingService) {} +} + +@Component({ + selector: 'missing', + template: 'missing', +}) +class MissingComponent { + constructor(public readonly missing: MissingService) {} +} + +@NgModule({}) +class MissingModule {} + +@Injectable() +class TargetService { + constructor(public readonly root: RootService) {} +} + +@Pipe({ + name: 'target', +}) +class TargetPipe implements PipeTransform { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} + + transform(): string { + return this.constructor.name; + } +} + +@Directive({ + selector: 'target', +}) +class TargetDirective { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} +} + +@Component({ + selector: 'target', + template: 'target', +}) +class TargetComponent { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective, TargetPipe], + providers: [TargetService], +}) +class TargetModule {} + +describe('issue-2647:errors', () => { + describe('strict', () => { + describe('.keep', () => { + it('throws on missing provided service', () => { + const builder = MockBuilder(TargetService, TargetModule).keep( + MissingService, + ); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingService/, + ); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService, TargetModule).keep( + RootService, + ); + expect(() => builder.build()).not.toThrow(); + }); + + it('throws on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).keep( + MissingPipe, + ); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingPipe/, + ); + }); + + it('throws on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).keep(MissingDirective); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingDirective/, + ); + }); + + it('throws on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).keep(MissingComponent); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingComponent/, + ); + }); + + it('throws on missing module', () => { + const builder = MockBuilder(TargetModule, []).keep( + MissingModule, + ); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingModule/, + ); + }); + }); + + describe('.mock', () => { + it('throws on missing provided service', () => { + const builder = MockBuilder(TargetService, TargetModule).mock( + MissingService, + ); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingService/, + ); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService, TargetModule).mock( + RootService, + ); + expect(() => builder.build()).not.toThrow(); + }); + + it('throws on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).mock( + MissingPipe, + ); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingPipe/, + ); + }); + + it('throws on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).mock(MissingDirective); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingDirective/, + ); + }); + + it('throws on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).mock(MissingComponent); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingComponent/, + ); + }); + + it('throws on missing module', () => { + const builder = MockBuilder(TargetModule, []).mock( + MissingModule, + ); + expect(() => builder.build()).toThrowError( + /MockBuilder has found a missing dependency: MissingModule/, + ); + }); + }); + + describe('.exclude', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder( + TargetService, + TargetModule, + ).exclude(MissingService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder( + TargetService, + TargetModule, + ).exclude(RootService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).exclude( + MissingPipe, + ); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).exclude(MissingDirective); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).exclude(MissingComponent); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule, []).exclude( + MissingModule, + ); + expect(() => builder.build()).not.toThrow(); + }); + }); + }); + + describe('flex', () => { + describe('.keep', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .keep(MissingService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .keep(RootService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .keep(MissingPipe); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .keep(MissingDirective); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .keep(MissingComponent); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule).keep(MissingModule); + expect(() => builder.build()).not.toThrow(); + }); + }); + + describe('.mock', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .mock(MissingService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .mock(RootService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .mock(MissingPipe); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .mock(MissingDirective); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .mock(MissingComponent); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule).mock(MissingModule); + expect(() => builder.build()).not.toThrow(); + }); + }); + + describe('.exclude', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .exclude(MissingService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .exclude(RootService); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .exclude(MissingPipe); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .exclude(MissingDirective); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .exclude(MissingComponent); + expect(() => builder.build()).not.toThrow(); + }); + + it('succeeds on missing module', () => { + const builder = + MockBuilder(TargetModule).exclude(MissingModule); + expect(() => builder.build()).not.toThrow(); + }); + }); + }); +}); diff --git a/tests/issue-2647/ignore.spec.ts b/tests/issue-2647/ignore.spec.ts new file mode 100644 index 0000000000..5765885869 --- /dev/null +++ b/tests/issue-2647/ignore.spec.ts @@ -0,0 +1,543 @@ +import { + Component, + Directive, + Injectable, + NgModule, + Pipe, + PipeTransform, + VERSION, +} from '@angular/core'; + +import { MockBuilder, ngMocks } from 'ng-mocks'; + +// @TODO remove with A5 support +const injectableRootServiceArgs = [ + { + providedIn: 'root', + } as never, +]; + +@Injectable(...injectableRootServiceArgs) +class RootService {} + +@Injectable() +class MissingService {} + +@Pipe({ + name: 'missing', +}) +class MissingPipe implements PipeTransform { + constructor(public readonly missing: MissingService) {} + + transform(): string { + return this.constructor.name; + } +} + +@Directive({ + selector: 'missing', +}) +class MissingDirective { + constructor(public readonly missing: MissingService) {} +} + +@Component({ + selector: 'missing', + template: 'missing', +}) +class MissingComponent { + constructor(public readonly missing: MissingService) {} +} + +@NgModule({}) +class MissingModule {} + +@Injectable() +class TargetService { + constructor(public readonly root: RootService) {} +} + +@Pipe({ + name: 'target', +}) +class TargetPipe implements PipeTransform { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} + + transform(): string { + return this.constructor.name; + } +} + +@Directive({ + selector: 'target', +}) +class TargetDirective { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} +} + +@Component({ + selector: 'target', + template: 'target', +}) +class TargetComponent { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective, TargetPipe], + providers: [TargetService], +}) +class TargetModule {} + +describe('issue-2647:ignore', () => { + let consoleWarn: typeof console.warn; + + beforeAll(() => + ngMocks.config({ + onMockBuilderMissingDependency: 'i-know-but-disable', + }), + ); + beforeAll(() => (consoleWarn = console.warn)); + + beforeEach(() => { + console.warn = + typeof jest === 'undefined' ? jasmine.createSpy() : jest.fn(); + }); + + afterAll(() => { + console.warn = consoleWarn; + ngMocks.config({ + onMockBuilderMissingDependency: null, + }); + }); + + describe('strict', () => { + describe('.keep', () => { + it('throws on missing provided service', () => { + const builder = MockBuilder(TargetService, TargetModule).keep( + MissingService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService, TargetModule).keep( + RootService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).keep( + MissingPipe, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).keep(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).keep(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing module', () => { + const builder = MockBuilder(TargetModule, []).keep( + MissingModule, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + + describe('.mock', () => { + it('throws on missing provided service', () => { + const builder = MockBuilder(TargetService, TargetModule).mock( + MissingService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService, TargetModule).mock( + RootService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).mock( + MissingPipe, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).mock(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).mock(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing module', () => { + const builder = MockBuilder(TargetModule, []).mock( + MissingModule, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + + describe('.exclude', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder( + TargetService, + TargetModule, + ).exclude(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder( + TargetService, + TargetModule, + ).exclude(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).exclude( + MissingPipe, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).exclude(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).exclude(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule, []).exclude( + MissingModule, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + }); + + describe('flex', () => { + describe('.keep', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .keep(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .keep(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .keep(MissingPipe); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .keep(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .keep(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule).keep(MissingModule); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + + describe('.mock', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .mock(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .mock(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .mock(MissingPipe); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .mock(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .mock(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule).mock(MissingModule); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + + describe('.exclude', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .exclude(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .exclude(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .exclude(MissingPipe); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .exclude(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .exclude(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = + MockBuilder(TargetModule).exclude(MissingModule); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/tests/issue-2647/test.spec.ts b/tests/issue-2647/test.spec.ts new file mode 100644 index 0000000000..97bcabb5e2 --- /dev/null +++ b/tests/issue-2647/test.spec.ts @@ -0,0 +1,64 @@ +import { Component, Directive, NgModule } from '@angular/core'; + +import { MockBuilder, MockRender } from 'ng-mocks'; + +@Directive({ + selector: 'target', +}) +class TargetDirective {} + +@NgModule({ + declarations: [TargetDirective], + exports: [TargetDirective], +}) +class DirectiveModule {} + +@NgModule({ + imports: [DirectiveModule], +}) +class MiddleModule {} + +@Component({ + selector: 'target', + template: `{{ directive.constructor.name }}`, +}) +class TargetComponent { + constructor(public readonly directive: TargetDirective) {} +} + +@NgModule({ + imports: [DirectiveModule], + declarations: [TargetComponent], +}) +class TargetModule {} + +// @see https://github.com/ike18t/ng-mocks/issues/2647 +describe('issue-2647', () => { + describe('chain', () => { + beforeEach(() => + MockBuilder(TargetComponent) + .mock(MiddleModule) + .keep(DirectiveModule), + ); + + it('exports the service', () => { + const component = + MockRender(TargetComponent).point.componentInstance; + expect(component.directive).toBeDefined(); + }); + }); + + describe('params', () => { + beforeEach(() => + MockBuilder(TargetComponent, TargetModule).keep( + DirectiveModule, + ), + ); + + it('exports the service', () => { + const component = + MockRender(TargetComponent).point.componentInstance; + expect(component.directive).toBeDefined(); + }); + }); +}); diff --git a/tests/issue-2647/warn.spec.ts b/tests/issue-2647/warn.spec.ts new file mode 100644 index 0000000000..0d079f6ca9 --- /dev/null +++ b/tests/issue-2647/warn.spec.ts @@ -0,0 +1,561 @@ +import { + Component, + Directive, + Injectable, + NgModule, + Pipe, + PipeTransform, + VERSION, +} from '@angular/core'; + +import { MockBuilder, ngMocks } from 'ng-mocks'; + +// @TODO remove with A5 support +const injectableRootServiceArgs = [ + { + providedIn: 'root', + } as never, +]; + +@Injectable(...injectableRootServiceArgs) +class RootService {} + +@Injectable() +class MissingService {} + +@Pipe({ + name: 'missing', +}) +class MissingPipe implements PipeTransform { + constructor(public readonly missing: MissingService) {} + + transform(): string { + return this.constructor.name; + } +} + +@Directive({ + selector: 'missing', +}) +class MissingDirective { + constructor(public readonly missing: MissingService) {} +} + +@Component({ + selector: 'missing', + template: 'missing', +}) +class MissingComponent { + constructor(public readonly missing: MissingService) {} +} + +@NgModule({}) +class MissingModule {} + +@Injectable() +class TargetService { + constructor(public readonly root: RootService) {} +} + +@Pipe({ + name: 'target', +}) +class TargetPipe implements PipeTransform { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} + + transform(): string { + return this.constructor.name; + } +} + +@Directive({ + selector: 'target', +}) +class TargetDirective { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} +} + +@Component({ + selector: 'target', + template: 'target', +}) +class TargetComponent { + constructor( + public readonly target: TargetService, + public readonly root: RootService, + ) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective, TargetPipe], + providers: [TargetService], +}) +class TargetModule {} + +describe('issue-2647:warn', () => { + let consoleWarn: typeof console.warn; + + beforeAll(() => + ngMocks.config({ onMockBuilderMissingDependency: 'warn' }), + ); + beforeAll(() => (consoleWarn = console.warn)); + + beforeEach(() => { + console.warn = + typeof jest === 'undefined' ? jasmine.createSpy() : jest.fn(); + }); + + afterAll(() => { + console.warn = consoleWarn; + ngMocks.config({ + onMockBuilderMissingDependency: null, + }); + }); + + describe('strict', () => { + describe('.keep', () => { + it('throws on missing provided service', () => { + const builder = MockBuilder(TargetService, TargetModule).keep( + MissingService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingService/, + ); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService, TargetModule).keep( + RootService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).keep( + MissingPipe, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingPipe/, + ); + }); + + it('throws on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).keep(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingDirective/, + ); + }); + + it('throws on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).keep(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingComponent/, + ); + }); + + it('throws on missing module', () => { + const builder = MockBuilder(TargetModule, []).keep( + MissingModule, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingModule/, + ); + }); + }); + + describe('.mock', () => { + it('throws on missing provided service', () => { + const builder = MockBuilder(TargetService, TargetModule).mock( + MissingService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingService/, + ); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService, TargetModule).mock( + RootService, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('throws on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).mock( + MissingPipe, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingPipe/, + ); + }); + + it('throws on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).mock(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingDirective/, + ); + }); + + it('throws on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).mock(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingComponent/, + ); + }); + + it('throws on missing module', () => { + const builder = MockBuilder(TargetModule, []).mock( + MissingModule, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalledWith( + /MockBuilder has found a missing dependency: MissingModule/, + ); + }); + }); + + describe('.exclude', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder( + TargetService, + TargetModule, + ).exclude(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder( + TargetService, + TargetModule, + ).exclude(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe, TargetModule).exclude( + MissingPipe, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder( + TargetDirective, + TargetModule, + ).exclude(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder( + TargetComponent, + TargetModule, + ).exclude(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule, []).exclude( + MissingModule, + ); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + }); + + describe('flex', () => { + describe('.keep', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .keep(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .keep(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .keep(MissingPipe); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .keep(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .keep(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule).keep(MissingModule); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + + describe('.mock', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .mock(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .mock(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .mock(MissingPipe); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .mock(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .mock(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = MockBuilder(TargetModule).mock(MissingModule); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + + describe('.exclude', () => { + it('succeeds on missing provided service', () => { + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .exclude(MissingService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on root service', () => { + if (Number.parseInt(VERSION.major, 10) <= 5) { + // @TODO pending('Need Angular > 5'); + expect(true).toBeTruthy(); + + return; + } + + const builder = MockBuilder(TargetService) + .mock(TargetModule) + .exclude(RootService); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing pipe ', () => { + const builder = MockBuilder(TargetPipe) + .mock(TargetModule) + .exclude(MissingPipe); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing directive', () => { + const builder = MockBuilder(TargetDirective) + .mock(TargetModule) + .exclude(MissingDirective); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing component', () => { + const builder = MockBuilder(TargetComponent) + .mock(TargetModule) + .exclude(MissingComponent); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + + it('succeeds on missing module', () => { + const builder = + MockBuilder(TargetModule).exclude(MissingModule); + + expect(console.warn).not.toHaveBeenCalled(); + builder.build(); + expect(console.warn).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/tests/issue-312/test.spec.ts b/tests/issue-312/test.spec.ts index caf9661865..73fd065786 100644 --- a/tests/issue-312/test.spec.ts +++ b/tests/issue-312/test.spec.ts @@ -11,6 +11,7 @@ import { TestBed } from '@angular/core/testing'; import { MockBuilder, MockRender } from 'ng-mocks'; +// @TODO remove with A5 support const injectableRootServiceArgs = [ { providedIn: 'root', diff --git a/tests/issue-333/test.spec.ts b/tests/issue-333/test.spec.ts index 8f2e3b2f1d..c762edca0f 100644 --- a/tests/issue-333/test.spec.ts +++ b/tests/issue-333/test.spec.ts @@ -51,9 +51,10 @@ describe('issue-333', () => { describe('1:keep', () => { // this should work with and without ivy beforeEach(() => - MockBuilder(DynamicOverlayComponent, OverlayModule) - .keep(MockComponent) - .keep(DepComponent), + MockBuilder( + [DynamicOverlayComponent, MockComponent, DepComponent], + OverlayModule, + ), ); it('should render', () => { @@ -76,9 +77,10 @@ describe('issue-333', () => { describe('2:mock', () => { // this should work with and without ivy beforeEach(() => - MockBuilder(DynamicOverlayComponent, OverlayModule) - .mock(MockComponent) - .keep(CommonModule, { export: true }), + MockBuilder(DynamicOverlayComponent, [ + OverlayModule, + MockComponent, + ]).keep(CommonModule, { export: true }), ); it('renders a mock component', () => { diff --git a/tests/issue-455/abstract.spec.ts b/tests/issue-455/abstract.spec.ts index 8be9003305..d71c970409 100644 --- a/tests/issue-455/abstract.spec.ts +++ b/tests/issue-455/abstract.spec.ts @@ -18,6 +18,7 @@ interface InjectedAbstraction { hello: () => number; } +// @TODO remove with A5 support const injectableArgs = [ { providedIn: 'root', diff --git a/tests/issue-488/test.spec.ts b/tests/issue-488/test.spec.ts index 1dfba0c912..8c1023a361 100644 --- a/tests/issue-488/test.spec.ts +++ b/tests/issue-488/test.spec.ts @@ -32,7 +32,7 @@ describe('issue-488', () => { describe('classic', () => { beforeEach(() => { - service = TestBed.get(TargetService); + service = ngMocks.findInstance(TargetService); ngMocks.stubMember( service, 'method', @@ -51,7 +51,7 @@ describe('issue-488', () => { it('throws an error about usage of the injector', () => { const testBed: any = getTestBed(); - service = TestBed.get(TargetService); + service = ngMocks.findInstance(TargetService); ngMocks.stubMember( service, 'method', diff --git a/tests/issue-572/test.spec.ts b/tests/issue-572/test.spec.ts index 8a0b3f3f0d..0ee42daa7e 100644 --- a/tests/issue-572/test.spec.ts +++ b/tests/issue-572/test.spec.ts @@ -1,5 +1,4 @@ import { Component, Injector } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; @@ -36,7 +35,7 @@ describe('issue-572', () => { }); it('warns via console on TestBed change', () => { - TestBed.get(Injector); + ngMocks.findInstance(Injector); expect(console.warn).not.toHaveBeenCalled(); const fixture = MockRender(TargetComponent); expect(console.warn).toHaveBeenCalled(); @@ -48,7 +47,7 @@ describe('issue-572', () => { it('keeps the config', () => { ngMocks.config({}); - TestBed.get(Injector); + ngMocks.findInstance(Injector); expect(console.warn).not.toHaveBeenCalled(); MockRender(TargetComponent); expect(console.warn).toHaveBeenCalled(); @@ -58,7 +57,7 @@ describe('issue-572', () => { ngMocks.config({ onTestBedFlushNeed: 'throw' }); try { - TestBed.get(Injector); + ngMocks.findInstance(Injector); MockRender(TargetComponent); fail('should throw'); } catch (error) { @@ -74,7 +73,7 @@ describe('issue-572', () => { it('skips warnings on TestBed change', () => { ngMocks.config({ onTestBedFlushNeed: 'i-know-but-disable' }); - TestBed.get(Injector); + ngMocks.findInstance(Injector); expect(console.warn).not.toHaveBeenCalled(); const fixture = MockRender(TargetComponent); expect(console.warn).not.toHaveBeenCalled(); diff --git a/tests/issue-623/nested.spec.ts b/tests/issue-623/nested.spec.ts index e2d114b055..45bb85d914 100644 --- a/tests/issue-623/nested.spec.ts +++ b/tests/issue-623/nested.spec.ts @@ -61,7 +61,9 @@ describe('issue-623:nested', () => { target = 0; expect(ngMocks.formatText(factory())).toEqual('target:1'); expect(ngMocks.formatText(factory())).toEqual('target:1'); - expect(TestBed.get(TargetService).name).toEqual('target:1'); + expect(ngMocks.findInstance(TargetService).name).toEqual( + 'target:1', + ); }); }); }); diff --git a/tests/issue-625/test.spec.ts b/tests/issue-625/test.spec.ts index 1ed06dd136..f1d7bcdfe7 100644 --- a/tests/issue-625/test.spec.ts +++ b/tests/issue-625/test.spec.ts @@ -4,9 +4,13 @@ import { NgModule, OnInit, } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { MockBuilder, MockInstance, MockRender } from 'ng-mocks'; +import { + MockBuilder, + MockInstance, + MockRender, + ngMocks, +} from 'ng-mocks'; /* * As you can see, SomeService provided in forRoot() function. @@ -75,9 +79,10 @@ describe('issue-625', () => { let spy: any; beforeEach(() => { - return MockBuilder(MyComponent, MyModule) - .keep(SomeModule.forRoot()) - .mock(SomeService); + return MockBuilder( + [MyComponent, SomeModule.forRoot()], + MyModule, + ).mock(SomeService); }); beforeEach(() => { @@ -93,8 +98,12 @@ describe('issue-625', () => { MockRender(MyComponent); expect(spy).toHaveBeenCalled(); // mocks the service - expect(TestBed.get(SomeService).name).toEqual(undefined); + expect(ngMocks.findInstance(SomeService).name).toEqual( + undefined as any, + ); // keeps another one - expect(TestBed.get(AnotherService).name).toEqual('another'); + expect(ngMocks.findInstance(AnotherService).name).toEqual( + 'another', + ); }); }); diff --git a/tests/issue-762/string.spec.ts b/tests/issue-762/string.spec.ts index ece145cbfa..f835dac132 100644 --- a/tests/issue-762/string.spec.ts +++ b/tests/issue-762/string.spec.ts @@ -1,7 +1,6 @@ import { NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { MockBuilder } from 'ng-mocks'; +import { MockBuilder, ngMocks } from 'ng-mocks'; @NgModule({ providers: [ @@ -19,8 +18,8 @@ describe('issue-762:string', () => { beforeEach(() => MockBuilder('STRING')); it('works correctly', () => { - expect(() => TestBed.get('STRING')).toThrowError( - /No provider for STRING/, + expect(() => ngMocks.findInstance('STRING')).toThrowError( + 'Cannot find an instance via ngMocks.findInstance(STRING)', ); }); }); @@ -29,7 +28,7 @@ describe('issue-762:string', () => { beforeEach(() => MockBuilder('STRING', TargetModule)); it('works correctly', () => { - const token = TestBed.get('STRING'); + const token = ngMocks.findInstance('STRING'); expect(token).toEqual('TOKEN'); }); }); @@ -38,7 +37,7 @@ describe('issue-762:string', () => { beforeEach(() => MockBuilder(['STRING'], TargetModule)); it('works correctly', () => { - const token = TestBed.get('STRING'); + const token = ngMocks.findInstance('STRING'); expect(token).toEqual('TOKEN'); }); }); @@ -47,7 +46,7 @@ describe('issue-762:string', () => { beforeEach(() => MockBuilder(null, 'STRING')); it('works correctly', () => { - const token = TestBed.get('STRING'); + const token = ngMocks.findInstance('STRING'); expect(token).toEqual(undefined); }); }); @@ -56,7 +55,7 @@ describe('issue-762:string', () => { beforeEach(() => MockBuilder(TargetModule, 'STRING')); it('works correctly', () => { - const token = TestBed.get('STRING'); + const token = ngMocks.findInstance('STRING'); expect(token).toEqual(undefined); }); }); @@ -65,7 +64,7 @@ describe('issue-762:string', () => { beforeEach(() => MockBuilder(TargetModule, ['STRING'])); it('works correctly', () => { - const token = TestBed.get('STRING'); + const token = ngMocks.findInstance('STRING'); expect(token).toEqual(undefined); }); }); diff --git a/tests/mock-builder-keeps-application-module/test.spec.ts b/tests/mock-builder-keeps-application-module/test.spec.ts index 211adb4fdd..eb744befc5 100644 --- a/tests/mock-builder-keeps-application-module/test.spec.ts +++ b/tests/mock-builder-keeps-application-module/test.spec.ts @@ -1,5 +1,4 @@ import { APP_ID, APP_INITIALIZER, VERSION } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; @@ -19,9 +18,9 @@ describe('MockBuilderKeepsApplicationModule:real', () => { TargetComponent, ); expect(element).toBeDefined(); - expect(TestBed.get(TARGET_TOKEN)).toBeDefined(); - expect(TestBed.get(APP_INITIALIZER)).toBeDefined(); - expect(TestBed.get(APP_ID)).toBeDefined(); + expect(ngMocks.findInstance(TARGET_TOKEN)).toBeDefined(); + expect(ngMocks.findInstance(APP_INITIALIZER)).toBeDefined(); + expect(ngMocks.findInstance(APP_ID)).toBeDefined(); }); }); @@ -35,12 +34,12 @@ describe('MockBuilderKeepsApplicationModule:mock', () => { TargetComponent, ); expect(element).toBeDefined(); - expect(TestBed.get(TARGET_TOKEN)).toEqual(''); + expect(ngMocks.findInstance(TARGET_TOKEN)).toEqual(''); if (Number.parseInt(VERSION.major, 10) < 9) { // somehow ivy does not provide APP_INITIALIZER out of the box and this assertion fails. // our mock logic skips all multi tokens therefore this one is not present anymore. - expect(TestBed.get(APP_INITIALIZER)).toBeDefined(); + expect(ngMocks.findInstance(APP_INITIALIZER)).toBeDefined(); } - expect(TestBed.get(APP_ID)).toBeDefined(); + expect(ngMocks.findInstance(APP_ID)).toBeDefined(); }); }); diff --git a/tests/mock-instance-in-it/test.spec.ts b/tests/mock-instance-in-it/test.spec.ts index 7a1bb11be9..7603da1209 100644 --- a/tests/mock-instance-in-it/test.spec.ts +++ b/tests/mock-instance-in-it/test.spec.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { MockBuilder, MockInstance } from 'ng-mocks'; +import { MockBuilder, MockInstance, ngMocks } from 'ng-mocks'; @Injectable() class TargetService { @@ -35,18 +34,18 @@ describe('mock-instance-in-it', () => { echo: () => 'it', })); - const actual = TestBed.get(TargetService).echo(); + const actual = ngMocks.findInstance(TargetService).echo(); expect(actual).toEqual('it'); }); it('uses beforeEach in the 2nd it', () => { - const actual = TestBed.get(TargetService).echo(); + const actual = ngMocks.findInstance(TargetService).echo(); expect(actual).toEqual('beforeEach'); }); }); it('receives default value', () => { - const actual = TestBed.get(TargetService).echo(); + const actual = ngMocks.findInstance(TargetService).echo(); expect(actual).toEqual('beforeAll'); }); }); diff --git a/tests/mock-instance-token/test.spec.ts b/tests/mock-instance-token/test.spec.ts index 14ae10969d..2ca3cb68e8 100644 --- a/tests/mock-instance-token/test.spec.ts +++ b/tests/mock-instance-token/test.spec.ts @@ -4,9 +4,8 @@ import { InjectionToken, NgModule, } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { MockBuilder, MockInstance } from 'ng-mocks'; +import { MockBuilder, MockInstance, ngMocks } from 'ng-mocks'; const TOKEN = new InjectionToken('TOKEN'); @@ -39,7 +38,7 @@ describe('mock-instance-token', () => { it('provides tokens', () => { MockInstance(TOKEN, () => 'mock'); - const actual = TestBed.get(TargetService).echo(); + const actual = ngMocks.findInstance(TargetService).echo(); expect(actual).toEqual('mock'); }); }); diff --git a/tests/module-with-factory-tokens/test.spec.ts b/tests/module-with-factory-tokens/test.spec.ts index 07c823971c..92e25e932b 100644 --- a/tests/module-with-factory-tokens/test.spec.ts +++ b/tests/module-with-factory-tokens/test.spec.ts @@ -49,9 +49,10 @@ describe('module-with-factory-tokens:keep', () => { } beforeEach(() => - MockBuilder(TargetComponent, TargetModule) - .keep(MY_TOKEN_SINGLE) - .keep(MY_TOKEN_MULTI), + MockBuilder( + [TargetComponent, MY_TOKEN_SINGLE, MY_TOKEN_MULTI], + TargetModule, + ), ); it('renders all tokens', () => { @@ -89,9 +90,11 @@ describe('module-with-factory-tokens:mock-0', () => { // Result of the render is an empty string because there is no way to pass multi. describe('module-with-factory-tokens:mock-1', () => { beforeEach(() => - MockBuilder(TargetComponent, TargetModule) - .mock(MY_TOKEN_SINGLE) - .mock(MY_TOKEN_MULTI), + MockBuilder(TargetComponent, [ + TargetModule, + MY_TOKEN_SINGLE, + MY_TOKEN_MULTI, + ]), ); it('renders all tokens', () => { @@ -107,8 +110,8 @@ describe('module-with-factory-tokens:mock-1', () => { describe('module-with-factory-tokens:mock-2', () => { beforeEach(() => MockBuilder(TargetComponent, TargetModule) - .mock(MY_TOKEN_SINGLE, 'MOCK_MY_TOKEN_SINGLE') - .mock(MY_TOKEN_MULTI, 'MOCK_MY_TOKEN_MULTI'), + .mock(MY_TOKEN_SINGLE, 'MOCK_MY_TOKEN_SINGLE', { export: true }) + .mock(MY_TOKEN_MULTI, 'MOCK_MY_TOKEN_MULTI', { export: true }), ); it('renders all tokens', () => { @@ -136,9 +139,10 @@ describe('module-with-factory-tokens:mock-3', () => { } beforeEach(() => - MockBuilder(TargetComponent, TargetModule) - .keep(MY_TOKEN_SINGLE) - .keep(MY_TOKEN_MULTI), + MockBuilder( + [TargetComponent, MY_TOKEN_SINGLE, MY_TOKEN_MULTI], + TargetModule, + ), ); it('renders all tokens', () => { diff --git a/tests/ng-mocks-default-mock/test.precise.spec.ts b/tests/ng-mocks-default-mock/test.precise.spec.ts index e033a2ba33..bd30528445 100644 --- a/tests/ng-mocks-default-mock/test.precise.spec.ts +++ b/tests/ng-mocks-default-mock/test.precise.spec.ts @@ -1,5 +1,4 @@ import { Injectable, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, ngMocks } from 'ng-mocks'; @@ -38,8 +37,8 @@ describe('ng-mocks-default-mock:precise', () => { ); it('overrides default mock', () => { - const s1 = TestBed.get(Service1); - const s2 = TestBed.get(Service2); + const s1 = ngMocks.findInstance(Service1); + const s2 = ngMocks.findInstance(Service2); // extended. expect(s1).not.toBe(m1); diff --git a/tests/ng-mocks-default-mock/test.unset.spec.ts b/tests/ng-mocks-default-mock/test.unset.spec.ts index cec5d6c951..12ee73d169 100644 --- a/tests/ng-mocks-default-mock/test.unset.spec.ts +++ b/tests/ng-mocks-default-mock/test.unset.spec.ts @@ -32,8 +32,8 @@ describe('ng-mocks-default-mock:unset', () => { ); it('unsets defaultMock', () => { - const t1 = TestBed.get(TOKEN1); - const t2 = TestBed.get(TOKEN2); + const t1 = ngMocks.findInstance(TOKEN1); + const t2 = ngMocks.findInstance(TOKEN2); // default mock. expect(t1).toEqual('mockToken1'); diff --git a/tests/providedin-root/test.spec.ts b/tests/providedin-root/test.spec.ts index 860ce92275..f20b7e8962 100644 --- a/tests/providedin-root/test.spec.ts +++ b/tests/providedin-root/test.spec.ts @@ -15,6 +15,7 @@ const TOKEN = new (InjectionToken as any)('TOKEN', { providedIn: 'root', }); +// @TODO remove with A5 support const injectableServiceArgs = [ { providedIn: 'root', diff --git a/tests/provider-with-custom-dependencies/test.spec.ts b/tests/provider-with-custom-dependencies/test.spec.ts index 96a4a92ea9..8e6edf24ae 100644 --- a/tests/provider-with-custom-dependencies/test.spec.ts +++ b/tests/provider-with-custom-dependencies/test.spec.ts @@ -9,8 +9,9 @@ import { } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { MockBuilder, MockRender } from 'ng-mocks'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; +// @TODO remove with A5 support const injectableDep1ServiceArgs = [ { providedIn: 'root', @@ -22,6 +23,7 @@ class Dep1Service { public readonly name = 'dep-1'; } +// @TODO remove with A5 support const injectableDep2ServiceArgs = [ { providedIn: 'root', @@ -111,8 +113,8 @@ describe('provider-with-custom-dependencies', () => { '"optional:missed"', ); // The dependency should not be provided in TestBed. - expect(() => TestBed.get(Dep3Service)).toThrowError( - /No provider for Dep3Service/, + expect(() => ngMocks.findInstance(Dep3Service)).toThrowError( + 'Cannot find an instance via ngMocks.findInstance(Dep3Service)', ); }); }); @@ -131,8 +133,8 @@ describe('provider-with-custom-dependencies', () => { '"optional:missed"', ); // The dependency should not be provided in TestBed. - expect(() => TestBed.get(Dep3Service)).toThrowError( - /No provider for Dep3Service/, + expect(() => ngMocks.findInstance(Dep3Service)).toThrowError( + 'Cannot find an instance via ngMocks.findInstance(Dep3Service)', ); }); }); @@ -141,9 +143,7 @@ describe('provider-with-custom-dependencies', () => { beforeEach(() => MockBuilder(TargetComponent, TargetModule) .keep(TargetService) - .keep(Dep2Service, { - dependency: true, - }), + .exclude(Dep2Service), ); it('creates component with kept Dep2Service', () => { @@ -157,8 +157,8 @@ describe('provider-with-custom-dependencies', () => { '"optional:missed"', ); // The dependency should not be provided in TestBed. - expect(() => TestBed.get(Dep3Service)).toThrowError( - /No provider for Dep3Service/, + expect(() => ngMocks.findInstance(Dep3Service)).toThrowError( + 'Cannot find an instance via ngMocks.findInstance(Dep3Service)', ); }); }); diff --git a/tests/root-provider-with-root-dep/test.spec.ts b/tests/root-provider-with-root-dep/test.spec.ts index c2a757db6d..0f75287eab 100644 --- a/tests/root-provider-with-root-dep/test.spec.ts +++ b/tests/root-provider-with-root-dep/test.spec.ts @@ -8,13 +8,14 @@ import { } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { MockBuilder, MockRender } from 'ng-mocks'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; // Thanks A5. const TOKEN = new (InjectionToken as any)('TOKEN', { factory: () => 'token', }); +// @TODO remove with A5 support const injectableTargetServiceArgs = [ { providedIn: 'root', @@ -77,7 +78,7 @@ describe('root-provider-with-root-dep', () => { const fixture = MockRender(TargetComponent); expect(fixture.nativeElement.innerHTML).toContain('"name:"'); // A nested token as a dependency should be replaced with a mock copy. - expect(TestBed.get(TOKEN)).toBeUndefined(); + expect(ngMocks.findInstance(TOKEN, undefined)).toBeUndefined(); }); }); }); diff --git a/tests/root-providers/fixtures.ts b/tests/root-providers/fixtures.ts index eb87bc7f72..cfd4020926 100644 --- a/tests/root-providers/fixtures.ts +++ b/tests/root-providers/fixtures.ts @@ -12,6 +12,7 @@ export class ModuleService { public readonly name = 'module'; } +// @TODO remove with A5 support const injectableTargetServiceArgs = [ { providedIn: 'root', @@ -23,6 +24,7 @@ export class TargetService { public readonly name = 'service'; } +// @TODO remove with A5 support const injectableFakeServiceArgs = [ { providedIn: 'root', diff --git a/tests/root-providers/test.spec.ts b/tests/root-providers/test.spec.ts index d5a49adb92..f6522be0a2 100644 --- a/tests/root-providers/test.spec.ts +++ b/tests/root-providers/test.spec.ts @@ -97,7 +97,7 @@ describe('root-providers', () => { TargetService, TargetService, { - dependency: true, + dependency: false, }, ), ); @@ -118,7 +118,9 @@ describe('root-providers', () => { describe('keep', () => { beforeEach(() => - MockBuilder(TargetComponent, TargetModule).keep(TargetService), + MockBuilder(TargetComponent, TargetModule).exclude( + TargetService, + ), ); it('uses mock providers', () => { @@ -184,9 +186,9 @@ describe('root-providers', () => { describe('keep as dependency', () => { beforeEach(() => - MockBuilder(TargetComponent, TargetModule).keep(TargetService, { - dependency: true, - }), + MockBuilder(TargetComponent, TargetModule).exclude( + TargetService, + ), ); it('uses mock providers', () => { diff --git a/tests/tokens-class/test.spec.ts b/tests/tokens-class/test.spec.ts index f379e6281f..710699389a 100644 --- a/tests/tokens-class/test.spec.ts +++ b/tests/tokens-class/test.spec.ts @@ -1,5 +1,4 @@ import { Injectable, InjectionToken, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, ngMocks } from 'ng-mocks'; @@ -56,25 +55,25 @@ describe('tokens-class', () => { ); it('resolves Class1Service as a mock instance', () => { - const actual = TestBed.get(Class1Service); + const actual = ngMocks.findInstance(Class1Service); expect(actual).toEqual(assertion.any(Class1Service)); expect(actual.name).toBeUndefined(); }); it('resolves Class2Service as a real instance', () => { - const actual = TestBed.get(Class2Service); + const actual = ngMocks.findInstance(Class2Service); expect(actual).toEqual(assertion.any(Class2Service)); expect(actual.name).toEqual('class2'); }); it('resolves TOKEN_EXISTING_MOCK as a mock instance', () => { - const actual = TestBed.get(TOKEN_CLASS_MOCK); + const actual = ngMocks.findInstance(TOKEN_CLASS_MOCK); expect(actual).toEqual(assertion.any(Class1Service)); expect(actual.name).toBeUndefined(); }); it('resolves TOKEN_EXISTING_KEEP as a real instance', () => { - const actual = TestBed.get(TOKEN_CLASS_KEEP); + const actual = ngMocks.findInstance(TOKEN_CLASS_KEEP); expect(actual).toEqual(assertion.any(Class2Service)); expect(actual.name).toEqual('class2'); }); diff --git a/tests/tokens-existing/test.spec.ts b/tests/tokens-existing/test.spec.ts index d516b16a2b..5f3952366f 100644 --- a/tests/tokens-existing/test.spec.ts +++ b/tests/tokens-existing/test.spec.ts @@ -1,5 +1,4 @@ import { Injectable, InjectionToken, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, ngMocks } from 'ng-mocks'; @@ -46,13 +45,13 @@ describe('tokens-existing', () => { ); it('resolves TOKEN_EXISTING_MOCK as a mock service', () => { - const actual = TestBed.get(TOKEN_EXISTING_MOCK); + const actual = ngMocks.findInstance(TOKEN_EXISTING_MOCK); expect(actual).toEqual(assertion.any(Exist1Service)); expect(actual.name).toBeUndefined(); }); it('resolves TOKEN_EXISTING_KEEP as a real service', () => { - const actual = TestBed.get(TOKEN_EXISTING_KEEP); + const actual = ngMocks.findInstance(TOKEN_EXISTING_KEEP); expect(actual).toEqual(assertion.any(Exist2Service)); expect(actual.name).toEqual('exist2'); }); diff --git a/tests/tokens-factory/test.spec.ts b/tests/tokens-factory/test.spec.ts index a68dd82579..bdf659844f 100644 --- a/tests/tokens-factory/test.spec.ts +++ b/tests/tokens-factory/test.spec.ts @@ -1,7 +1,6 @@ import { InjectionToken, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; -import { MockBuilder } from 'ng-mocks'; +import { MockBuilder, ngMocks } from 'ng-mocks'; const TOKEN_FACTORY1 = new InjectionToken('FACTORY1'); const TOKEN_FACTORY2 = new InjectionToken('FACTORY2'); @@ -29,10 +28,10 @@ describe('tokens-factory', () => { // If a factory returns something else - it should be replaced with a mock copy manually // with a proper value. it('mocks TOKEN_FACTORY as an empty object', () => { - const actual1 = TestBed.get(TOKEN_FACTORY1); + const actual1 = ngMocks.findInstance(TOKEN_FACTORY1); expect(actual1).toEqual({}); - const actual2 = TestBed.get(TOKEN_FACTORY2); + const actual2 = ngMocks.findInstance(TOKEN_FACTORY2); expect(actual2).toEqual({}); }); }); diff --git a/tests/tokens-value/test.spec.ts b/tests/tokens-value/test.spec.ts index cf27c3ab74..c8a3ac45a5 100644 --- a/tests/tokens-value/test.spec.ts +++ b/tests/tokens-value/test.spec.ts @@ -1,5 +1,4 @@ import { InjectionToken, NgModule } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import { MockBuilder, ngMocks } from 'ng-mocks'; @@ -55,7 +54,7 @@ describe('tokens-value', () => { beforeEach(() => MockBuilder().mock(TargetModule)); it('mocks TOKEN_OBJECT via MockService', () => { - const actual = TestBed.get(TOKEN_OBJECT); + const actual = ngMocks.findInstance(TOKEN_OBJECT); expect(actual).toEqual({ func: assertion.anything(), }); @@ -63,27 +62,27 @@ describe('tokens-value', () => { }); it('mocks TOKEN_CLASS as undefined', () => { - const actual = TestBed.get(TOKEN_CLASS); + const actual = ngMocks.findInstance(TOKEN_CLASS); expect(actual).toBeUndefined(); }); it('mocks TOKEN_BOOLEAN as false', () => { - const actual = TestBed.get(TOKEN_BOOLEAN); + const actual = ngMocks.findInstance(TOKEN_BOOLEAN); expect(actual).toBe(false); }); it('mocks TOKEN_NUMBER as 0', () => { - const actual = TestBed.get(TOKEN_NUMBER); + const actual = ngMocks.findInstance(TOKEN_NUMBER); expect(actual).toBe(0); }); it('mocks TOKEN_STRING as an empty string', () => { - const actual = TestBed.get(TOKEN_STRING); + const actual = ngMocks.findInstance(TOKEN_STRING); expect(actual).toBe(''); }); it('mocks TOKEN_NULL as null', () => { - const actual = TestBed.get(TOKEN_NULL); + const actual = ngMocks.findInstance(TOKEN_NULL); expect(actual).toBe(null); }); });