diff --git a/README.md b/README.md index a114a64053..7c8c077fbb 100644 --- a/README.md +++ b/README.md @@ -1381,6 +1381,9 @@ and has a rich toolkit that supports: * [`exportAll` flag](#mockbuilder-exportall-flag) * [`dependency` flag](#mockbuilder-dependency-flag) * [`render` flag](#mockbuilder-render-flag) +* [`NG_MOCKS_GUARDS` token](#ng_mocks_guards-token) +* [`NG_MOCKS_INTERCEPTORS` token](#ng_mocks_interceptors-token) +* [`NG_MOCKS_ROOT_PROVIDERS` token](#ng_mocks_root_providers-token) * [Good to know](#mockbuilder-good-to-know)
Click to see a code sample demonstrating ease of mocking in Angular tests @@ -1543,23 +1546,6 @@ beforeEach(() => ); ``` -If we want to test guards we need to `.keep` them, but what should we do with other guards we do not want to care about at all? -The answer is to exclude `NG_GUARDS` token, it will removal all the guards from their routes except the explicitly configured ones. - -```typescript -beforeEach(() => MockBuilder(MyGuard, MyModule).exclude(NG_GUARDS)); -``` - -The same thing if we want to test interceptors. -If we exclude `NG_INTERCEPTORS` token, then all interceptors with `useValue` or `useFactory` will be excluded -together with other interceptors except the explicitly configured ones. - -```typescript -beforeEach(() => - MockBuilder(MyInterceptor, MyModule).exclude(NG_INTERCEPTORS) -); -``` - #### MockBuilder.replace If we want to replace something with something, we should use `.replace`. @@ -1721,6 +1707,61 @@ beforeEach(() => ); ``` +#### `NG_MOCKS_GUARDS` token + +If we want to test guards we need to `.keep` them, but what should we do with other guards we do not want to care about at all? +The answer is to exclude `NG_MOCKS_GUARDS` token, it will **remove all the guards from routes** except the explicitly configured ones. + +```typescript +beforeEach(() => + MockBuilder(MyGuard, MyModule).exclude(NG_MOCKS_GUARDS) +); +``` + +#### `NG_MOCKS_INTERCEPTORS` token + +Usually, when we want to test an interceptor, we want to avoid influences of other interceptors. +To **remove all interceptors in an angular test** we need to exclude `NG_MOCKS_INTERCEPTORS` token, +then all interceptors will be excluded except the explicitly configured ones. + +```typescript +beforeEach(() => + MockBuilder(MyInterceptor, MyModule).exclude(NG_MOCKS_INTERCEPTORS) +); +``` + +#### `NG_MOCKS_ROOT_PROVIDERS` token + +There are root services and tokens apart from provided ones in Angular applications. +It might happen that in a test we want these providers to be mocked, or kept. + +If we want to mock all root providers in an angular test we need to mock `NG_MOCKS_ROOT_PROVIDERS` token. + +```typescript +beforeEach(() => + MockBuilder( + MyComponentWithRootServices, + MyModuleWithRootTokens + ).mock(NG_MOCKS_ROOT_PROVIDERS) +); +``` + +In contrast to that, we might want to keep all root providers for mocked declarations. +For that, we need to keep `NG_MOCKS_ROOT_PROVIDERS` token. + +```typescript +beforeEach(() => + MockBuilder( + MyComponentWithRootServices, + MyModuleWithRootTokens + ).keep(NG_MOCKS_ROOT_PROVIDERS) +); +``` + +If we do not pass `NG_MOCKS_ROOT_PROVIDERS` anywhere, +then only root providers for kept modules will stay as they are. +All other root providers will be mocked, even for kept declarations of mocked modules. + #### MockBuilder good to know Anytime we can change our decision. The last action on the same object wins. SomeModule will be mocked. @@ -2978,13 +3019,13 @@ If you did not read ["How to test a route"](#how-to-test-a-route), please do it To test a guard means that we need to mock everything except the guard and `RouterModule`. But, what if we have several guards? If we mocked them they would block routes due to falsy returns of their mocked methods. -**To skip guards in angular tests `ngMocks` provides `NG_GUARDS` token**, we should pass it into `.exclude`, then all other guards will be +**To skip guards in angular tests `ngMocks` provides `NG_MOCKS_GUARDS` token**, we should pass it into `.exclude`, then all other guards will be excluded from `TestBed` and we can be sure, that we are **testing only the guard we want**. ```typescript beforeEach(() => MockBuilder(LoginGuard, TargetModule) - .exclude(NG_GUARDS) + .exclude(NG_MOCKS_GUARDS) .keep(RouterModule) .keep(RouterTestingModule.withRoutes([])) ); @@ -3027,7 +3068,7 @@ Optionally, we can disable guards to avoid influence of their mocked methods ret ```typescript beforeEach(() => MockBuilder(DataResolver, TargetModule) - .exclude(NG_GUARDS) + .exclude(NG_MOCKS_GUARDS) .keep(RouterModule) .keep(RouterTestingModule.withRoutes([])) ); @@ -3147,7 +3188,7 @@ The problem of `useValue` and `useFactory` is that it is quite hard to distingui in `TestBed`. We need to keep `HTTP_INTERCEPTORS` token, because the interceptor is defined by it. -But this cause that all other interceptors will be kept too, therefore, we need to get rid of them via excluding `NG_INTERCEPTORS` token. +But this cause that all other interceptors will be kept too, therefore, we need to get rid of them via excluding `NG_MOCKS_INTERCEPTORS` token. The issue here is that if there are more interceptors, then their mocked copies will fail with "You provided 'undefined' where a stream was expected." error. And the last important step is to replace `HttpClientModule` with `HttpClientTestingModule`, @@ -3156,7 +3197,7 @@ so we can use `HttpTestingController` for faking requests. ```typescript beforeEach(() => MockBuilder(TargetInterceptor, TargetModule) - .exclude(NG_INTERCEPTORS) + .exclude(NG_MOCKS_INTERCEPTORS) .keep(HTTP_INTERCEPTORS) .replace(HttpClientModule, HttpClientTestingModule) ); diff --git a/examples/TestHttpInterceptor/test.spec.ts b/examples/TestHttpInterceptor/test.spec.ts index 508fa80292..cf28f88b33 100644 --- a/examples/TestHttpInterceptor/test.spec.ts +++ b/examples/TestHttpInterceptor/test.spec.ts @@ -10,7 +10,7 @@ import { import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { Injectable, NgModule } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { MockBuilder, NG_INTERCEPTORS } from 'ng-mocks'; +import { MockBuilder, NG_MOCKS_INTERCEPTORS } from 'ng-mocks'; import { Observable } from 'rxjs'; // An interceptor we want to test. @@ -73,7 +73,7 @@ describe('TestHttpInterceptor', () => { // with HttpClientTestingModule. beforeEach(() => MockBuilder(TargetInterceptor, TargetModule) - .exclude(NG_INTERCEPTORS) + .exclude(NG_MOCKS_INTERCEPTORS) .keep(HTTP_INTERCEPTORS) .replace(HttpClientModule, HttpClientTestingModule) ); diff --git a/examples/TestRoutingGuard/test.spec.ts b/examples/TestRoutingGuard/test.spec.ts index 9868207e1b..8110b9b024 100644 --- a/examples/TestRoutingGuard/test.spec.ts +++ b/examples/TestRoutingGuard/test.spec.ts @@ -3,7 +3,7 @@ import { Component, Injectable, NgModule, VERSION } from '@angular/core'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { CanActivate, Router, RouterModule, RouterOutlet } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { MockBuilder, MockRender, NG_GUARDS, ngMocks } from 'ng-mocks'; +import { MockBuilder, MockRender, NG_MOCKS_GUARDS, ngMocks } from 'ng-mocks'; import { from, Observable } from 'rxjs'; import { mapTo } from 'rxjs/operators'; @@ -99,9 +99,12 @@ describe('TestRoutingGuard', () => { // RouterModule to have its routes, and to add // RouterTestingModule.withRoutes([]), yes yes, with empty routes // to have tools for testing. And the last thing is to exclude - // `NG_GUARDS` to remove all other guards. + // `NG_MOCKS_GUARDS` to remove all other guards. beforeEach(() => - MockBuilder(LoginGuard, TargetModule).exclude(NG_GUARDS).keep(RouterModule).keep(RouterTestingModule.withRoutes([])) + MockBuilder(LoginGuard, TargetModule) + .exclude(NG_MOCKS_GUARDS) + .keep(RouterModule) + .keep(RouterTestingModule.withRoutes([])) ); // It is important to run routing tests in fakeAsync. diff --git a/lib/common/core.config.ts b/lib/common/core.config.ts new file mode 100644 index 0000000000..22a6b4b839 --- /dev/null +++ b/lib/common/core.config.ts @@ -0,0 +1,17 @@ +import { CommonModule } from '@angular/common'; +import { ApplicationModule } from '@angular/core'; + +export default { + neverMockModule: [ApplicationModule, CommonModule], + neverMockProvidedFunction: [ + 'DomRendererFactory2', + 'EventManager', + 'Injector', // ivy only + 'RendererFactory2', + ], + neverMockToken: [ + 'InjectionToken Set Injector scope.', // INJECTOR_SCOPE // ivy only + 'InjectionToken EventManagerPlugins', // EVENT_MANAGER_PLUGINS + 'InjectionToken HammerGestureConfig', // HAMMER_GESTURE_CONFIG + ], +}; diff --git a/lib/common/core.tokens.ts b/lib/common/core.tokens.ts index dcdba581cc..1d9ed2b191 100644 --- a/lib/common/core.tokens.ts +++ b/lib/common/core.tokens.ts @@ -8,5 +8,20 @@ export const NG_MOCKS_TOUCHES = new InjectionToken>('NG_MOCKS_TOUCHES') export const NG_MOCKS_OVERRIDES = new InjectionToken | AbstractType, MetadataOverride>>( 'NG_MOCKS_OVERRIDES' ); -export const NG_GUARDS = new InjectionToken('NG_MOCKS_GUARDS'); -export const NG_INTERCEPTORS = new InjectionToken('NG_MOCKS_INTERCEPTORS'); +export const NG_MOCKS_GUARDS = new InjectionToken('NG_MOCKS_GUARDS'); +export const NG_MOCKS_INTERCEPTORS = new InjectionToken('NG_MOCKS_INTERCEPTORS'); +export const NG_MOCKS_ROOT_PROVIDERS = new InjectionToken('NG_MOCKS_ROOT_PROVIDERS'); + +/** + * Use NG_MOCKS_GUARDS instead. + * Will be removed in v11. + * @deprecated + */ +export const NG_GUARDS = NG_MOCKS_GUARDS; + +/** + * Use NG_MOCKS_INTERCEPTORS instead. + * Will be removed in v11. + * @deprecated + */ +export const NG_INTERCEPTORS = NG_MOCKS_INTERCEPTORS; diff --git a/lib/mock-builder/mock-builder-promise.skip-dep.ts b/lib/mock-builder/mock-builder-promise.skip-dep.ts index 09edb4288c..3c29dfbd06 100644 --- a/lib/mock-builder/mock-builder-promise.skip-dep.ts +++ b/lib/mock-builder/mock-builder-promise.skip-dep.ts @@ -1,6 +1,7 @@ import { DOCUMENT } from '@angular/common'; -import { EVENT_MANAGER_PLUGINS } from '@angular/platform-browser'; +import { isNgInjectionToken } from 'ng-mocks'; +import ngConfig from '../common/core.config'; import { ngMocksUniverse } from '../common/ng-mocks-universe'; // Checks if we should avoid mocking of the provider. @@ -11,10 +12,15 @@ export default (provide: any): boolean => { if (ngMocksUniverse.touches.has(provide)) { return true; } + if (provide === DOCUMENT) { return true; } - if (provide === EVENT_MANAGER_PLUGINS) { + + if (typeof provide === 'function' && ngConfig.neverMockProvidedFunction.indexOf(provide.name) !== -1) { + return true; + } + if (isNgInjectionToken(provide) && ngConfig.neverMockToken.indexOf(provide.toString()) !== -1) { return true; } diff --git a/lib/mock-builder/mock-builder-promise.ts b/lib/mock-builder/mock-builder-promise.ts index 857f1b2e45..a8a31dbf9f 100644 --- a/lib/mock-builder/mock-builder-promise.ts +++ b/lib/mock-builder/mock-builder-promise.ts @@ -2,8 +2,8 @@ import { InjectionToken, NgModule, PipeTransform, Provider } from '@angular/core import { MetadataOverride, TestBed } from '@angular/core/testing'; import { extractDependency, flatten, mapEntries, mapValues } from '../common/core.helpers'; -import { directiveResolver, jitReflector, ngModuleResolver } from '../common/core.reflect'; -import { NG_MOCKS, NG_MOCKS_OVERRIDES, NG_MOCKS_TOUCHES } from '../common/core.tokens'; +import { directiveResolver, jitReflector } from '../common/core.reflect'; +import { NG_MOCKS, NG_MOCKS_OVERRIDES, NG_MOCKS_ROOT_PROVIDERS, NG_MOCKS_TOUCHES } from '../common/core.tokens'; import { AnyType, Type } from '../common/core.types'; import { isNgDef } from '../common/func.is-ng-def'; import { isNgInjectionToken } from '../common/func.is-ng-injection-token'; @@ -63,21 +63,27 @@ export class MockBuilderPromise implements PromiseLike { ngMocksUniverse.touches = new Set(); ngMocksUniverse.config.set('multi', new Set()); // collecting multi flags of providers. ngMocksUniverse.config.set('deps', new Set()); // collecting all deps of providers. + ngMocksUniverse.config.set('depsSkip', new Set()); // collecting all declarations of kept modules. + ngMocksUniverse.config.set('resolution', new Map()); // flags to understand how to mock nested declarations. for (const def of mapValues(this.keepDef)) { ngMocksUniverse.builder.set(def, def); + ngMocksUniverse.config.get('resolution').set(def, 'keep'); } - for (const source of mapValues(this.replaceDef)) { - ngMocksUniverse.builder.set(source, this.defValue.get(source)); + for (const def of mapValues(this.replaceDef)) { + ngMocksUniverse.builder.set(def, this.defValue.get(def)); + ngMocksUniverse.config.get('resolution').set(def, 'replace'); } for (const def of [...mapValues(this.excludeDef)]) { ngMocksUniverse.builder.set(def, null); + ngMocksUniverse.config.get('resolution').set(def, 'exclude'); } // mocking requested things. for (const def of mapValues(this.mockDef)) { + ngMocksUniverse.config.get('resolution').set(def, 'mock'); if (isNgDef(def)) { continue; } @@ -111,7 +117,7 @@ export class MockBuilderPromise implements PromiseLike { // Now we need to run through requested modules. const defProviders = new Map(); - for (const def of [...mapValues(this.mockDef), ...mapValues(this.keepDef), ...mapValues(this.replaceDef)]) { + for (const def of [...mapValues(this.keepDef), ...mapValues(this.mockDef), ...mapValues(this.replaceDef)]) { if (!isNgDef(def, 'm')) { continue; } @@ -213,25 +219,43 @@ export class MockBuilderPromise implements PromiseLike { } } - // Adding missed providers. + // Mocking root providers. const parameters = new Set(); - if (ngMocksUniverse.touches.size || ngMocksUniverse.config.get('deps').size) { - const touchedDefs: any[] = mapValues(ngMocksUniverse.touches); - touchedDefs.push(...mapValues(ngMocksUniverse.config.get('deps'))); - for (const def of touchedDefs) { - if (!skipDep(def)) { - parameters.add(def); - } - - for (const decorators of jitReflector.parameters(def)) { - const provide: any = extractDep(decorators); - if (skipDep(provide)) { - continue; + if (!this.keepDef.has(NG_MOCKS_ROOT_PROVIDERS)) { + // We need buckets here to process first all depsSkip, then deps and only after that all other defs. + const buckets: any[] = []; + buckets.push(mapValues(ngMocksUniverse.config.get('depsSkip'))); + buckets.push(mapValues(ngMocksUniverse.config.get('deps'))); + buckets.push(mapValues(ngMocksUniverse.touches)); + // Also we need to track what has been touched to check params recursively, but avoiding duplicates. + const touched: any[] = [].concat(...buckets); + for (const bucket of buckets) { + for (const def of bucket) { + if (!skipDep(def)) { + if (this.mockDef.has(NG_MOCKS_ROOT_PROVIDERS) || !ngMocksUniverse.config.get('depsSkip').has(def)) { + parameters.add(def); + } } - if (typeof provide === 'function' && touchedDefs.indexOf(provide) === -1) { - touchedDefs.push(provide); + + for (const decorators of jitReflector.parameters(def)) { + const provide: any = extractDep(decorators); + if (skipDep(provide)) { + continue; + } + if (ngMocksUniverse.config.get('depsSkip').has(provide)) { + continue; + } + if (typeof provide === 'function' && touched.indexOf(provide) === -1) { + touched.push(provide); + bucket.push(provide); + } + + if (this.mockDef.has(NG_MOCKS_ROOT_PROVIDERS) || !ngMocksUniverse.config.get('depsSkip').has(def)) { + parameters.add(provide); + } else { + ngMocksUniverse.config.get('depsSkip').add(provide); + } } - parameters.add(provide); } } } @@ -301,9 +325,7 @@ export class MockBuilderPromise implements PromiseLike { } let meta: NgModule | undefined; - if (isNgDef(value, 'm')) { - meta = ngModuleResolver.resolve(value); - } else if (isNgDef(value, 'c')) { + if (isNgDef(value, 'c')) { meta = directiveResolver.resolve(value); } else if (isNgDef(value, 'd')) { meta = directiveResolver.resolve(value); @@ -316,7 +338,7 @@ export class MockBuilderPromise implements PromiseLike { if (!skipMock) { ngMocksUniverse.flags.add('skipMock'); } - const [changed, def] = MockNgDef(meta); + const [changed, def] = MockNgDef({ providers: meta.providers }); /* istanbul ignore else */ if (!skipMock) { ngMocksUniverse.flags.delete('skipMock'); diff --git a/lib/mock-builder/mock-builder.ts b/lib/mock-builder/mock-builder.ts index b7ededf082..3b26c1ba8f 100644 --- a/lib/mock-builder/mock-builder.ts +++ b/lib/mock-builder/mock-builder.ts @@ -53,9 +53,7 @@ export function MockBuilder( for (const [def, override] of mapEntries(overrides)) { (TestBed as any).ngMocksOverrides.add(def); /* istanbul ignore else */ - if (isNgDef(def, 'm')) { - testBed.overrideModule(def, override); - } else if (isNgDef(def, 'c')) { + if (isNgDef(def, 'c')) { testBed.overrideComponent(def, override); } else if (isNgDef(def, 'd')) { testBed.overrideDirective(def, override); @@ -81,9 +79,7 @@ export function MockBuilder( ngMocks.flushTestBed(); for (const def of (TestBed as any).ngMocksOverrides) { /* istanbul ignore else */ - if (isNgDef(def, 'm')) { - TestBed.overrideModule(def, {}); - } else if (isNgDef(def, 'c')) { + if (isNgDef(def, 'c')) { TestBed.overrideComponent(def, {}); } else if (isNgDef(def, 'd')) { TestBed.overrideDirective(def, {}); diff --git a/lib/mock-module/mock-module.ts b/lib/mock-module/mock-module.ts index 3b7490d1f0..2727a86736 100644 --- a/lib/mock-module/mock-module.ts +++ b/lib/mock-module/mock-module.ts @@ -1,8 +1,8 @@ -import { CommonModule } from '@angular/common'; import { core } from '@angular/compiler'; -import { ApplicationModule, NgModule, Provider } from '@angular/core'; +import { NgModule, Provider } from '@angular/core'; import { getTestBed } from '@angular/core/testing'; +import ngConfig from '../common/core.config'; import { extendClass, flatten } from '../common/core.helpers'; import { ngModuleResolver } from '../common/core.reflect'; import { Type } from '../common/core.types'; @@ -32,7 +32,7 @@ export function MockModule(module: any): any { let mockModule: typeof ngModule | undefined; let mockModuleProviders: typeof ngModuleProviders; let mockModuleDef: NgModule | undefined; - let releaseSkipMockFlag = false; + let toggleSkipMockFlag = false; if (isNgModuleDefWithProviders(module)) { ngModule = module.ngModule; @@ -43,10 +43,6 @@ export function MockModule(module: any): any { ngModule = module; } - if (NEVER_MOCK.indexOf(ngModule) !== -1) { - return module; - } - // We are inside of an 'it'. // It's fine to to return a mock or to throw an exception if it wasn't mocked in TestBed. if (!ngModuleProviders && (getTestBed() as any)._instantiated) { @@ -63,16 +59,33 @@ export function MockModule(module: any): any { mockModule = ngMocksUniverse.cacheMocks.get(ngModule); } + const resolution: undefined | 'mock' | 'keep' | 'replace' | 'exclude' = ngMocksUniverse.config + .get('resolution') + ?.get(ngModule); + if (resolution === 'mock' && ngMocksUniverse.flags.has('skipMock')) { + toggleSkipMockFlag = true; + ngMocksUniverse.flags.delete('skipMock'); + } + if (resolution === 'keep' && !ngMocksUniverse.flags.has('skipMock')) { + toggleSkipMockFlag = true; + ngMocksUniverse.flags.add('skipMock'); + } + if (resolution === 'replace' && !ngMocksUniverse.flags.has('skipMock')) { + toggleSkipMockFlag = true; + ngMocksUniverse.flags.add('skipMock'); + } + + if (ngConfig.neverMockModule.indexOf(ngModule) !== -1 && !ngMocksUniverse.flags.has('skipMock')) { + toggleSkipMockFlag = true; + ngMocksUniverse.flags.add('skipMock'); + } + // Now we check if we need to keep the original module or to replace it with some other. if (!mockModule && ngMocksUniverse.builder.has(ngModule)) { const instance = ngMocksUniverse.builder.get(ngModule); if (isNgDef(instance, 'm') && instance !== ngModule) { mockModule = instance; } - if (!ngMocksUniverse.flags.has('skipMock')) { - releaseSkipMockFlag = true; - ngMocksUniverse.flags.add('skipMock'); - } } if (!mockModule) { @@ -97,23 +110,31 @@ export function MockModule(module: any): any { // the last thing is to apply decorators. NgModule(mockModuleDef)(mockModule as any); MockOf(ngModule)(mockModule as any); - - /* istanbul ignore else */ - if (ngMocksUniverse.flags.has('cacheModule')) { - ngMocksUniverse.cacheMocks.set(ngModule, mockModule); - } } if (!mockModule) { mockModule = ngModule; } + // We should always cache the result, in global scope it always will be a mock. + // In MockBuilder scope it will be reset later anyway. + /* istanbul ignore else */ + if (ngMocksUniverse.flags.has('cacheModule')) { + ngMocksUniverse.cacheMocks.set(ngModule, mockModule); + } + + if (ngMocksUniverse.flags.has('skipMock')) { + ngMocksUniverse.config.get('depsSkip')?.add(mockModule); + } + if (ngModuleProviders) { const [changed, ngModuleDef] = MockNgDef({ providers: ngModuleProviders }); mockModuleProviders = changed ? ngModuleDef.providers : ngModuleProviders; } - if (releaseSkipMockFlag) { + if (toggleSkipMockFlag && ngMocksUniverse.flags.has('skipMock')) { ngMocksUniverse.flags.delete('skipMock'); + } else if (toggleSkipMockFlag && !ngMocksUniverse.flags.has('skipMock')) { + ngMocksUniverse.flags.add('skipMock'); } return mockModule === ngModule && mockModuleProviders === ngModuleProviders @@ -123,8 +144,6 @@ export function MockModule(module: any): any { : mockModule; } -const NEVER_MOCK: Array> = [CommonModule, ApplicationModule]; - /** * Can be changed at any time. * @@ -192,6 +211,10 @@ export function MockNgDef(ngModuleDef: NgModule, ngModule?: Type): [boolean mockedDef = MockPipe(def); } + if (ngMocksUniverse.flags.has('skipMock')) { + ngMocksUniverse.config.get('depsSkip')?.add(mockedDef); + } + resolutions.set(def, mockedDef); changed = changed || mockedDef !== def; return mockedDef; diff --git a/lib/mock-service/helper.replace-with-mocks.ts b/lib/mock-service/helper.replace-with-mocks.ts index 71328d17f0..810715f41d 100644 --- a/lib/mock-service/helper.replace-with-mocks.ts +++ b/lib/mock-service/helper.replace-with-mocks.ts @@ -1,4 +1,4 @@ -import { NG_GUARDS } from '../common/core.tokens'; +import { NG_MOCKS_GUARDS } from '../common/core.tokens'; import { ngMocksUniverse } from '../common/ng-mocks-universe'; const replaceWithMocks = (value: any): any => { @@ -45,7 +45,7 @@ const replaceWithMocks = (value: any): any => { guards.push(guard); continue; } - if (ngMocksUniverse.builder.has(NG_GUARDS) && ngMocksUniverse.builder.get(NG_GUARDS) === null) { + if (ngMocksUniverse.builder.has(NG_MOCKS_GUARDS) && ngMocksUniverse.builder.get(NG_MOCKS_GUARDS) === null) { continue; } guards.push(guard); diff --git a/lib/mock-service/helper.resolve-provider.ts b/lib/mock-service/helper.resolve-provider.ts index c84a2a49dd..b9900e49eb 100644 --- a/lib/mock-service/helper.resolve-provider.ts +++ b/lib/mock-service/helper.resolve-provider.ts @@ -1,5 +1,5 @@ import { extractDependency } from '../common/core.helpers'; -import { NG_INTERCEPTORS } from '../common/core.tokens'; +import { NG_MOCKS_INTERCEPTORS } from '../common/core.tokens'; import { isNgInjectionToken } from '../common/func.is-ng-injection-token'; import { ngMocksUniverse } from '../common/ng-mocks-universe'; @@ -43,8 +43,8 @@ export default (def: any, resolutions: Map, changed?: (flag: boolean) } if ( - ngMocksUniverse.builder.has(NG_INTERCEPTORS) && - ngMocksUniverse.builder.get(NG_INTERCEPTORS) === null && + ngMocksUniverse.builder.has(NG_MOCKS_INTERCEPTORS) && + ngMocksUniverse.builder.get(NG_MOCKS_INTERCEPTORS) === null && isNgInjectionToken(provider) && provider.toString() === 'InjectionToken HTTP_INTERCEPTORS' && provider !== def @@ -80,6 +80,7 @@ export default (def: any, resolutions: Map, changed?: (flag: boolean) } if (!mockedDef && ngMocksUniverse.flags.has('skipMock')) { + ngMocksUniverse.config.get('depsSkip')?.add(provider); mockedDef = def; } if (!mockedDef) { diff --git a/lib/mock-service/mock-provider.ts b/lib/mock-service/mock-provider.ts index 0c54e34c75..58521dbf5c 100644 --- a/lib/mock-service/mock-provider.ts +++ b/lib/mock-service/mock-provider.ts @@ -1,20 +1,13 @@ -import { APP_INITIALIZER, Provider } from '@angular/core'; -import { EVENT_MANAGER_PLUGINS, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser'; +import { Provider } from '@angular/core'; +import ngConfig from '../common/core.config'; import { isNgInjectionToken } from '../common/func.is-ng-injection-token'; import { ngMocksUniverse } from '../common/ng-mocks-universe'; import useFactory from './helper.use-factory'; import { MockService } from './mock-service'; -const neverMockProvidedFunction = [ - 'DomRendererFactory2', - 'DomSharedStylesHost', - 'EventManager', - 'Injector', - 'RendererFactory2', -]; -const neverMockToken = [APP_INITIALIZER, EVENT_MANAGER_PLUGINS, HAMMER_GESTURE_CONFIG]; +const { neverMockProvidedFunction, neverMockToken } = ngConfig; export default function (provider: any): Provider | undefined { const provide = typeof provider === 'object' && provider.provide ? provider.provide : provider; @@ -22,7 +15,7 @@ export default function (provider: any): Provider | undefined { if (typeof provide === 'function' && neverMockProvidedFunction.indexOf(provide.name) !== -1) { return provider; } - if (isNgInjectionToken(provide) && neverMockToken.indexOf(provide) !== -1) { + if (isNgInjectionToken(provide) && neverMockToken.indexOf(provide.toString()) !== -1) { return undefined; } diff --git a/lib/mock-service/mock-service.spec.ts b/lib/mock-service/mock-service.spec.ts index 0bee447f55..22d7156c82 100644 --- a/lib/mock-service/mock-service.spec.ts +++ b/lib/mock-service/mock-service.spec.ts @@ -5,7 +5,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { InjectionToken, NgModule } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { NG_GUARDS, NG_INTERCEPTORS } from '../common/core.tokens'; +import { NG_MOCKS_GUARDS, NG_MOCKS_INTERCEPTORS } from '../common/core.tokens'; import { ngMocksUniverse } from '../common/ng-mocks-universe'; import { MockBuilder } from '../mock-builder/mock-builder'; import { ngMocks } from '../mock-helper/mock-helper'; @@ -339,7 +339,7 @@ describe('replaceWithMocks', () => { }); }); - it('keeps all guards without excluding NG_GUARDS', async () => { + it('keeps all guards without excluding NG_MOCKS_GUARDS', async () => { @NgModule({ providers: [ { @@ -359,7 +359,7 @@ describe('replaceWithMocks', () => { }); }); - it('ignores all guards with excluded NG_GUARDS', async () => { + it('ignores all guards with excluded NG_MOCKS_GUARDS', async () => { @NgModule({ providers: [ { @@ -372,7 +372,7 @@ describe('replaceWithMocks', () => { }) class TargetModule {} - await MockBuilder().mock(TargetModule).keep('test').exclude(NG_GUARDS); + await MockBuilder().mock(TargetModule).keep('test').exclude(NG_MOCKS_GUARDS); const actual = TestBed.get('test'); expect(actual).toEqual({ canActivate: [], @@ -381,7 +381,7 @@ describe('replaceWithMocks', () => { }); describe('resolveProvider', () => { - it('ignores useFactory and useValue interceptors with excluded NG_INTERCEPTORS', async () => { + it('ignores useFactory and useValue interceptors with excluded NG_MOCKS_INTERCEPTORS', async () => { @NgModule({ imports: [HttpClientModule], providers: [ @@ -403,7 +403,7 @@ describe('resolveProvider', () => { .mock(TargetModule) .replace(HttpClientModule, HttpClientTestingModule) .keep(HTTP_INTERCEPTORS) - .exclude(NG_INTERCEPTORS); + .exclude(NG_MOCKS_INTERCEPTORS); const actual = TestBed.get(HTTP_INTERCEPTORS); expect(actual).not.toEqual(jasmine.arrayContaining([false, true])); }); diff --git a/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts b/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts new file mode 100644 index 0000000000..a665cb20b8 --- /dev/null +++ b/tests/NG_MOCKS_ROOT_PROVIDERS/test.spec.ts @@ -0,0 +1,108 @@ +import { Component, Injectable as InjectableSource, NgModule, VERSION } from '@angular/core'; +import { MockBuilder, MockRender, NG_MOCKS_ROOT_PROVIDERS } from 'ng-mocks'; + +// Because of A5 we need to cast Injectable to any type. +// But because of A10+ we need to do it via a middle function. +function Injectable(...args: any[]): any { + return InjectableSource(...args); +} + +@Injectable({ + providedIn: 'root', +}) +class Target1Service { + public readonly name = 'target-1'; +} + +@Component({ + selector: 'target-1', + template: `{{ service.name }}`, +}) +class Target1Component { + public readonly service: Target1Service; + + constructor(service: Target1Service) { + this.service = service; + } +} + +@NgModule({ + declarations: [Target1Component], + exports: [Target1Component], +}) +class Target1Module {} + +@Injectable({ + providedIn: 'root', +}) +class Target2Service { + public readonly name = 'target-2'; +} + +@Component({ + selector: 'target-2', + template: `{{ service.name }}`, +}) +class Target2Component { + public readonly service: Target2Service; + + constructor(service: Target2Service) { + this.service = service; + } +} + +@NgModule({ + declarations: [Target2Component], + exports: [Target2Component], +}) +class Target2Module {} + +@NgModule({ + exports: [Target1Module, Target2Module], + imports: [Target1Module, Target2Module], +}) +class CombinedModule {} + +describe('NG_MOCKS_ROOT_PROVIDERS', () => { + beforeEach(() => { + if (parseInt(VERSION.major, 10) <= 5) { + pending('Need Angular > 5'); + } + }); + + describe('default for a kept module', () => { + beforeEach(() => MockBuilder(Target1Component, CombinedModule).keep(Target1Module)); + + it('keeps its global service', () => { + const fixture = MockRender(Target1Component); + expect(fixture.nativeElement.innerHTML).toEqual('target-1'); + }); + }); + + describe('mock the token', () => { + beforeEach(() => MockBuilder(Target1Component, CombinedModule).keep(Target1Module).mock(NG_MOCKS_ROOT_PROVIDERS)); + + it('mocks global service for a kept module', () => { + const fixture = MockRender(Target1Component); + expect(fixture.nativeElement.innerHTML).toEqual(''); + }); + }); + + describe('default for a mocked module', () => { + beforeEach(() => MockBuilder(Target1Component, CombinedModule)); + + it('mocks its global service', () => { + const fixture = MockRender(Target1Component); + expect(fixture.nativeElement.innerHTML).toEqual(''); + }); + }); + + describe('keep the token', () => { + beforeEach(() => MockBuilder(Target1Component, CombinedModule).keep(NG_MOCKS_ROOT_PROVIDERS)); + + it('keeps global service for a mocked module', () => { + const fixture = MockRender(Target1Component); + expect(fixture.nativeElement.innerHTML).toEqual('target-1'); + }); + }); +}); diff --git a/tests/issue-222/app-initializer.spec.ts b/tests/issue-222/app-initializer.spec.ts new file mode 100644 index 0000000000..9aed974025 --- /dev/null +++ b/tests/issue-222/app-initializer.spec.ts @@ -0,0 +1,45 @@ +import { APP_BASE_HREF } from '@angular/common'; +import { Component, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { BrowserModule } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; + +@Component({ + selector: 'target', + template: ``, +}) +class TargetComponent {} + +@NgModule({ + declarations: [TargetComponent], + imports: [BrowserModule, RouterModule.forRoot([])], +}) +class TargetModule {} + +describe('issue-222:APP_INITIALIZER:mock', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule)); + + it('correctly handles APP_INITIALIZER in a mocked module', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toContain(''); + }); +}); + +describe('issue-222:APP_INITIALIZER:keep', () => { + beforeEach(() => MockBuilder(TargetComponent).keep(TargetModule).mock(APP_BASE_HREF, '')); + + it('correctly handles APP_INITIALIZER in a kept module', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toContain(''); + }); +}); + +describe('issue-222:APP_INITIALIZER:guts', () => { + beforeEach(() => TestBed.configureTestingModule(ngMocks.guts(TargetComponent, TargetModule)).compileComponents()); + + it('correctly handles APP_INITIALIZER in a kept module', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toContain(''); + }); +}); diff --git a/tests/issue-222/application-module.spec.ts b/tests/issue-222/application-module.spec.ts new file mode 100644 index 0000000000..9611825b11 --- /dev/null +++ b/tests/issue-222/application-module.spec.ts @@ -0,0 +1,18 @@ +import { ApplicationRef, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { BrowserModule } from '@angular/platform-browser'; +import { MockBuilder } from 'ng-mocks'; + +@NgModule({ + imports: [BrowserModule], +}) +class TargetModule {} + +describe('issue-222:application-module', () => { + beforeEach(() => MockBuilder(null, TargetModule)); + + it('does not mock its guts', () => { + const service = TestBed.get(ApplicationRef); + expect(service.viewCount).toBeDefined(); + }); +}); diff --git a/tests/issue-222/common-module.spec.ts b/tests/issue-222/common-module.spec.ts new file mode 100644 index 0000000000..97359ca0ec --- /dev/null +++ b/tests/issue-222/common-module.spec.ts @@ -0,0 +1,29 @@ +import { CommonModule } from '@angular/common'; +import { Component, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { MockModule, MockRender } from 'ng-mocks'; + +@Component({ + selector: 'target', + template: `target`, +}) +class TargetComponent {} + +@NgModule({ + declarations: [TargetComponent], + imports: [CommonModule], +}) +class TargetModule {} + +describe('issue-222:CommonModule', () => { + beforeEach(() => + TestBed.configureTestingModule({ + imports: [CommonModule, MockModule(CommonModule), MockModule(TargetModule)], + }) + ); + + it('correctly handles kept and mocked CommonModule', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toEqual(''); + }); +}); diff --git a/tests/issue-222/dom-shared-styles-host.spec.ts b/tests/issue-222/dom-shared-styles-host.spec.ts new file mode 100644 index 0000000000..a1fd77b934 --- /dev/null +++ b/tests/issue-222/dom-shared-styles-host.spec.ts @@ -0,0 +1,79 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { Component, NgModule } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; + +@Component({ + animations: [ + trigger('openClose', [ + state( + 'open', + style({ + backgroundColor: 'yellow', + height: '200px', + opacity: 1, + }) + ), + state( + 'closed', + style({ + backgroundColor: 'green', + height: '100px', + opacity: 0.5, + }) + ), + transition('open => closed', [animate('1s')]), + transition('closed => open', [animate('0.5s')]), + ]), + ], + selector: 'target', + template: `
The box is now {{ isOpen ? 'Open' : 'Closed' }}!
`, +}) +class TargetComponent { + isOpen = true; + + toggle() { + this.isOpen = !this.isOpen; + } +} + +@NgModule({ + declarations: [TargetComponent], + imports: [BrowserModule, BrowserAnimationsModule], +}) +class TargetModule {} + +describe('issue-222:DomSharedStylesHost:mock', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule)); + + it('correctly handles DomSharedStylesHost in a mocked module', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toContain('The box is now Open!'); + // Animations are mocked, therefore no styles. + expect(fixture.nativeElement.innerHTML).not.toContain('yellow'); + }); +}); + +describe('issue-222:DomSharedStylesHost:keep', () => { + beforeEach(() => MockBuilder(TargetComponent).keep(TargetModule)); + + it('correctly handles DomSharedStylesHost in a kept module', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toContain('The box is now Open!'); + // Animations are kept, therefore we should get styles. + expect(fixture.nativeElement.innerHTML).toContain('yellow'); + }); +}); + +describe('issue-222:DomSharedStylesHost:guts', () => { + beforeEach(() => TestBed.configureTestingModule(ngMocks.guts(TargetComponent, TargetModule)).compileComponents()); + + it('correctly handles DomSharedStylesHost in a mocked module', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.nativeElement.innerHTML).toContain('The box is now Open!'); + // Animations are mocked, therefore no styles. + expect(fixture.nativeElement.innerHTML).not.toContain('yellow'); + }); +}); diff --git a/tests/issue-222/injector-scope.spec.ts b/tests/issue-222/injector-scope.spec.ts new file mode 100644 index 0000000000..75314cb108 --- /dev/null +++ b/tests/issue-222/injector-scope.spec.ts @@ -0,0 +1,59 @@ +// tslint:disable:no-unnecessary-class + +import { Component, Inject, Injectable as InjectableSource, NgModule, PLATFORM_ID, VERSION } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { MockBuilder, MockRender } from 'ng-mocks'; + +// Because of A5 we need to cast Injectable to any type. +// But because of A10+ we need to do it via a middle function. +function Injectable(...args: any[]): any { + return InjectableSource(...args); +} + +@Injectable({ + providedIn: 'root', +}) +class KeepService { + public readonly id: any; + + constructor(@Inject(PLATFORM_ID) id: any) { + this.id = id; + } + + public echo(): any { + return this.id; + } +} + +@NgModule({}) +class KeepModule { + constructor(service: KeepService) { + service.echo(); + } +} + +@Component({ + selector: 'target', + template: `target`, +}) +class TargetComponent {} + +@NgModule({ + declarations: [TargetComponent], + imports: [BrowserModule, KeepModule], +}) +class TargetModule {} + +describe('issue-222:INJECTOR_SCOPE', () => { + beforeEach(() => { + if (parseInt(VERSION.major, 10) <= 5) { + pending('Need Angular > 5'); + } + }); + + beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(KeepModule)); + + it('does not mock INJECTOR_SCOPE, fails on ivy only', () => { + expect(() => MockRender(TargetComponent)).not.toThrowError(/No provider for KeepService/); + }); +}); diff --git a/tests/issue-222/injector.spec.ts b/tests/issue-222/injector.spec.ts new file mode 100644 index 0000000000..729294d415 --- /dev/null +++ b/tests/issue-222/injector.spec.ts @@ -0,0 +1,35 @@ +import { Component, Injectable, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { MockBuilder, MockRender } from 'ng-mocks'; + +@Injectable() +class MockService {} + +@Component({ + selector: 'target', + template: `target`, +}) +class TargetComponent { + public readonly service: MockService; + + constructor(service: MockService) { + this.service = service; + } +} + +@NgModule({ + declarations: [TargetComponent], + exports: [TargetComponent], + imports: [BrowserModule], + providers: [MockService], +}) +class TargetModule {} + +describe('issue-222:Injector', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule)); + + it('does not mock Injector, fails on ivy only', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.point.componentInstance.service).toBeDefined(); + }); +}); diff --git a/tests/issue-222/kept-root-injection.spec.ts b/tests/issue-222/kept-root-injection.spec.ts new file mode 100644 index 0000000000..3ef0fbca84 --- /dev/null +++ b/tests/issue-222/kept-root-injection.spec.ts @@ -0,0 +1,78 @@ +// tslint:disable:no-unnecessary-class + +import { Component, Injectable as InjectableSource, NgModule, VERSION } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { MockBuilder, NG_MOCKS_ROOT_PROVIDERS } from 'ng-mocks'; + +// Because of A5 we need to cast Injectable to any type. +// But because of A10+ we need to do it via a middle function. +function Injectable(...args: any[]): any { + return InjectableSource(...args); +} + +@Injectable({ + providedIn: 'root', +}) +class TargetService { + protected readonly name = 'target'; + + public echo(): string { + return this.name; + } +} + +@NgModule({}) +class MockModule { + constructor(service: TargetService) { + service.echo(); + } +} + +@NgModule({}) +class KeepModule { + constructor(service: TargetService) { + service.echo(); + } +} + +@Component({ + selector: 'target', + template: 'target', +}) +class TargetComponent {} + +@NgModule({ + declarations: [TargetComponent], + imports: [MockModule, KeepModule], +}) +export class TargetModule {} + +// The problem here is that by the logic we keep KeepModule and mock MockModule. +// Both of them use a root provider TargetService. +// Because we keep KeepModule the TargetService has to be kept too. +// If we want to mock it, then we need to mock NG_MOCKS_ROOT_PROVIDERS token. +describe('issue-222:kept-root-injection', () => { + beforeEach(() => { + if (parseInt(VERSION.major, 10) <= 5) { + pending('Need Angular > 5'); + } + }); + + describe('real', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(KeepModule)); + + it('does not mock kept dependency', () => { + const service: TargetService = TestBed.get(TargetService); + expect(service.echo()).toBeDefined(); + }); + }); + + describe('NG_MOCKS_ROOT_PROVIDERS', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule).mock(NG_MOCKS_ROOT_PROVIDERS).keep(KeepModule)); + + it('does not mock kept dependency', () => { + const service: TargetService = TestBed.get(TargetService); + expect(service.echo()).toBeUndefined(); + }); + }); +}); diff --git a/tests/issue-222/mock-keep-priorities.spec.ts b/tests/issue-222/mock-keep-priorities.spec.ts new file mode 100644 index 0000000000..f3fccba8cf --- /dev/null +++ b/tests/issue-222/mock-keep-priorities.spec.ts @@ -0,0 +1,81 @@ +import { Component, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { MockBuilder, MockRender } from 'ng-mocks'; + +// This directive is shared via a module that is actually is kept in one and is mocked in another one. +@Component({ + selector: 'shared', + template: 'shared', +}) +class SharedComponent {} + +@NgModule({ + declarations: [SharedComponent], + exports: [SharedComponent], +}) +class SharedModule {} + +@NgModule({ + exports: [SharedModule], + imports: [SharedModule], +}) +class MockModule {} + +@NgModule({ + exports: [SharedModule], + imports: [SharedModule], +}) +class KeepModule {} + +@Component({ + selector: 'target', + template: 'target', +}) +class TargetComponent {} + +@NgModule({ + bootstrap: [TargetComponent], + declarations: [TargetComponent], + imports: [BrowserModule, BrowserAnimationsModule, KeepModule, MockModule], + providers: [], +}) +export class TargetModule {} + +describe('issue-222:mock-keep-priorities', () => { + describe('keep', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(KeepModule)); + + it('keeps all child imports', () => { + const fixture = MockRender(SharedComponent); + expect(fixture.nativeElement.innerHTML).toEqual('shared'); + }); + }); + + describe('mock', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(KeepModule).mock(SharedModule)); + + it('mocks the nested module of a kept module', () => { + const fixture = MockRender(SharedComponent); + expect(fixture.nativeElement.innerHTML).toEqual(''); + }); + }); + + describe('reverse', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule).mock(KeepModule).keep(SharedModule)); + + it('keeps the nested module of a mocked module', () => { + const fixture = MockRender(SharedComponent); + expect(fixture.nativeElement.innerHTML).toEqual('shared'); + }); + }); + + describe('mock keep priority', () => { + beforeEach(() => MockBuilder(TargetComponent, TargetModule).keep(KeepModule).mock(MockModule)); + + it('keep wins', () => { + const fixture = MockRender(SharedComponent); + expect(fixture.nativeElement.innerHTML).toEqual('shared'); + }); + }); +}); diff --git a/tests/root-providers/test.spec.ts b/tests/root-providers/test.spec.ts index c9d45b1243..70c3798a60 100644 --- a/tests/root-providers/test.spec.ts +++ b/tests/root-providers/test.spec.ts @@ -14,7 +14,7 @@ import { import { TestBed } from '@angular/core/testing'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; +import { MockBuilder, MockRender, NG_MOCKS_ROOT_PROVIDERS, ngMocks } from 'ng-mocks'; // Because of A5 we need to cast Injectable to any type. // But because of A10+ we need to do it via a middle function. @@ -184,7 +184,7 @@ describe('root-providers', () => { }); describe('keep via component module, but mocks root providers', () => { - beforeEach(() => MockBuilder(TargetModule)); + beforeEach(() => MockBuilder(TargetModule).mock(NG_MOCKS_ROOT_PROVIDERS)); it('mocks providers', () => { const fixture = MockRender(TargetComponent); @@ -197,7 +197,7 @@ describe('root-providers', () => { }); describe('keep via component module, and keeps root providers', () => { - beforeEach(() => MockBuilder(TargetModule).keep(TargetService).keep(TOKEN)); + beforeEach(() => MockBuilder(TargetModule).mock(NG_MOCKS_ROOT_PROVIDERS).keep(TargetService).keep(TOKEN)); it('mocks providers', () => { const fixture = MockRender(TargetComponent);