From d069a9047cc3188bea384632ffa1d3a0a62a09da Mon Sep 17 00:00:00 2001 From: satanTime Date: Thu, 2 Jun 2022 17:25:16 +0200 Subject: [PATCH] fix(MockBuilder): respect extention of classes with different decorators #2646 --- docs/articles/guides/component-provider.md | 8 +- docs/articles/guides/directive-provider.md | 28 +- examples/TestProviderInComponent/test.spec.ts | 8 +- examples/TestProviderInDirective/test.spec.ts | 28 +- .../lib/mock-builder/mock-builder.promise.ts | 4 - .../promise/add-missing-definition.ts | 20 -- ...d-missing-keep-declarations-and-modules.ts | 22 -- ...d-missing-mock-declarations-and-modules.ts | 18 -- .../lib/mock-builder/promise/init-modules.ts | 31 +- .../mock-builder/promise/init-ng-modules.ts | 35 ++- .../mock-builder/promise/skip-init-module.ts | 17 -- .../src/lib/mock-module/mock-ng-def.ts | 17 +- tests/issue-2646/test.spec.ts | 267 ++++++++++++++++++ 13 files changed, 369 insertions(+), 134 deletions(-) delete mode 100644 libs/ng-mocks/src/lib/mock-builder/promise/add-missing-definition.ts delete mode 100644 libs/ng-mocks/src/lib/mock-builder/promise/add-missing-keep-declarations-and-modules.ts delete mode 100644 libs/ng-mocks/src/lib/mock-builder/promise/add-missing-mock-declarations-and-modules.ts delete mode 100644 libs/ng-mocks/src/lib/mock-builder/promise/skip-init-module.ts create mode 100644 tests/issue-2646/test.spec.ts diff --git a/docs/articles/guides/component-provider.md b/docs/articles/guides/component-provider.md index 57112a8ba6..c2b7888444 100644 --- a/docs/articles/guides/component-provider.md +++ b/docs/articles/guides/component-provider.md @@ -30,7 +30,7 @@ const service = fixture.point.injector.get(TargetService); ```ts title="https://github.com/ike18t/ng-mocks/blob/master/examples/TestProviderInComponent/test.spec.ts" import { Component, Injectable } from '@angular/core'; -import { MockBuilder, MockRender } from 'ng-mocks'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; // A simple service, might have contained more logic, // but it is redundant for the test demonstration. @@ -61,9 +61,9 @@ describe('TestProviderInComponent', () => { // to access the service. const fixture = MockRender(TargetComponent); - // The root element is fixture.point and it is the TargetComponent - // with its injector for extracting internal services. - const service = fixture.point.injector.get(TargetService); + // The component's element is fixture.point. + // Now we can use ngMocks.get to extract internal services. + const service = ngMocks.get(fixture.point, TargetService); // Here we go, now we can assert everything about the service. expect(service.value).toEqual('target'); diff --git a/docs/articles/guides/directive-provider.md b/docs/articles/guides/directive-provider.md index 33985ee0d3..c9a07d79f3 100644 --- a/docs/articles/guides/directive-provider.md +++ b/docs/articles/guides/directive-provider.md @@ -82,26 +82,30 @@ describe('TestProviderInDirective', () => { beforeEach(() => MockBuilder(TargetService, TargetDirective)); it('has access to the service via a directive', () => { - // Let's render a div with the directive. It provides a point - // to access the service. - const fixture = MockRender('
'); + // Let's render a div with the directive. + MockRender('
'); - // The root element is fixture.point and it has access to the - // context of the directive. Its injector can extract the service. - const service = fixture.point.injector.get(TargetService); + // Let's find the debugElement with the directive. + // Please note, that we use ngMocks.find here. + const el = ngMocks.find(TargetDirective); + + // Let's extract the service. + const service = ngMocks.get(el, TargetService); // Here we go, now we can assert everything about the service. expect(service.value).toEqual(true); }); it('has access to the service via a structural directive', () => { - // Let's render a div with the directive. It provides a point to - // access the service. - const fixture = MockRender('
'); + // Let's render a div with the directive. + MockRender('
'); + + // Let's find the debugNode with the directive. + // Please note, that we use ngMocks.reveal here. + const node = ngMocks.reveal(TargetDirective); - // The root element is fixture.point and it has access to the - // context of the directive. Its injector can extract the service. - const service = fixture.point.injector.get(TargetService); + // Let's extract the service. + const service = ngMocks.get(node, TargetService); // Here we go, now we can assert everything about the service. expect(service.value).toEqual(true); diff --git a/examples/TestProviderInComponent/test.spec.ts b/examples/TestProviderInComponent/test.spec.ts index 88245cef19..37c60e5a67 100644 --- a/examples/TestProviderInComponent/test.spec.ts +++ b/examples/TestProviderInComponent/test.spec.ts @@ -1,6 +1,6 @@ import { Component, Injectable } from '@angular/core'; -import { MockBuilder, MockRender } from 'ng-mocks'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; // A simple service, might have contained more logic, // but it is redundant for the test demonstration. @@ -31,9 +31,9 @@ describe('TestProviderInComponent', () => { // to access the service. const fixture = MockRender(TargetComponent); - // The root element is fixture.point and it is the TargetComponent - // with its injector for extracting internal services. - const service = fixture.point.injector.get(TargetService); + // The component's element is fixture.point. + // Now we can use ngMocks.get to extract internal services. + const service = ngMocks.get(fixture.point, TargetService); // Here we go, now we can assert everything about the service. expect(service.value).toEqual('target'); diff --git a/examples/TestProviderInDirective/test.spec.ts b/examples/TestProviderInDirective/test.spec.ts index baffefca4f..146a2966df 100644 --- a/examples/TestProviderInDirective/test.spec.ts +++ b/examples/TestProviderInDirective/test.spec.ts @@ -49,26 +49,30 @@ describe('TestProviderInDirective', () => { beforeEach(() => MockBuilder(TargetService, TargetDirective)); it('has access to the service via a directive', () => { - // Let's render a div with the directive. It provides a point - // to access the service. - const fixture = MockRender('
'); + // Let's render a div with the directive. + MockRender('
'); - // The root element is fixture.point and it has access to the - // context of the directive. Its injector can extract the service. - const service = fixture.point.injector.get(TargetService); + // Let's find the debugElement with the directive. + // Please note, that we use ngMocks.find here. + const el = ngMocks.find(TargetDirective); + + // Let's extract the service. + const service = ngMocks.get(el, TargetService); // Here we go, now we can assert everything about the service. expect(service.value).toEqual(true); }); it('has access to the service via a structural directive', () => { - // Let's render a div with the directive. It provides a point to - // access the service. - const fixture = MockRender('
'); + // Let's render a div with the directive. + MockRender('
'); + + // Let's find the debugNode with the directive. + // Please note, that we use ngMocks.reveal here. + const node = ngMocks.reveal(TargetDirective); - // The root element is fixture.point and it has access to the - // context of the directive. Its injector can extract the service. - const service = fixture.point.injector.get(TargetService); + // Let's extract the service. + const service = ngMocks.get(node, TargetService); // Here we go, now we can assert everything about the service. expect(service.value).toEqual(true); 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 53a3f84365..720f8a0f78 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 @@ -9,8 +9,6 @@ import { isNgModuleDefWithProviders } from '../common/func.is-ng-module-def-with import ngMocksUniverse from '../common/ng-mocks-universe'; import { MockBuilderStash } from './mock-builder-stash'; -import addMissingKeepDeclarationsAndModules from './promise/add-missing-keep-declarations-and-modules'; -import addMissingMockDeclarationsAndModules from './promise/add-missing-mock-declarations-and-modules'; import addRequestedProviders from './promise/add-requested-providers'; import applyPlatformModules from './promise/apply-platform-modules'; import createNgMocksOverridesToken from './promise/create-ng-mocks-overrides-token'; @@ -80,8 +78,6 @@ export class MockBuilderPromise implements IMockBuilder { const ngModule = initNgModules(params, initUniverse(params)); detectWrongDeclarations(params); - addMissingKeepDeclarationsAndModules(ngModule, params); - addMissingMockDeclarationsAndModules(ngModule, params); addRequestedProviders(ngModule, params); handleRootProviders(ngModule, params); handleEntryComponents(ngModule); diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-definition.ts b/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-definition.ts deleted file mode 100644 index cca80ded6a..0000000000 --- a/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-definition.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { isNgDef } from '../../common/func.is-ng-def'; -import ngMocksUniverse from '../../common/ng-mocks-universe'; - -export default (def: any, configDef: Map): boolean => { - if (!isNgDef(def, 'i') && isNgDef(def)) { - return true; - } - - const config = configDef.get(def); - if (config?.dependency) { - return true; - } - - const configInstance = ngMocksUniverse.configInstance.get(def); - if (ngMocksUniverse.touches.has(def) && (configInstance?.exported || !config?.export)) { - return true; - } - - return false; -}; diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-keep-declarations-and-modules.ts b/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-keep-declarations-and-modules.ts deleted file mode 100644 index 4d3651a2ea..0000000000 --- a/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-keep-declarations-and-modules.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { mapValues } from '../../common/core.helpers'; -import { isNgInjectionToken } from '../../common/func.is-ng-injection-token'; -import ngMocksUniverse from '../../common/ng-mocks-universe'; - -import addMissingDefinition from './add-missing-definition'; -import { BuilderData, NgMeta } from './types'; - -export default (ngModule: NgMeta, { keepDef, configDef }: BuilderData): void => { - // Adding missed kept providers to test bed. - for (const def of mapValues(keepDef)) { - if (addMissingDefinition(def, configDef)) { - continue; - } - - if (isNgInjectionToken(def) || typeof def === 'string') { - ngMocksUniverse.touches.add(def); - continue; - } - ngModule.providers.push(def); - ngMocksUniverse.touches.add(def); - } -}; diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-mock-declarations-and-modules.ts b/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-mock-declarations-and-modules.ts deleted file mode 100644 index 9f33400134..0000000000 --- a/libs/ng-mocks/src/lib/mock-builder/promise/add-missing-mock-declarations-and-modules.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { mapValues } from '../../common/core.helpers'; -import ngMocksUniverse from '../../common/ng-mocks-universe'; - -import addMissingDefinition from './add-missing-definition'; -import { BuilderData, NgMeta } from './types'; - -export default (ngModule: NgMeta, { mockDef, configDef }: BuilderData): void => { - // Adding missed mock providers to test bed. - for (const def of mapValues(mockDef)) { - if (addMissingDefinition(def, configDef)) { - continue; - } - - const mock = ngMocksUniverse.builtProviders.get(def); - ngModule.providers.push(mock); - ngMocksUniverse.touches.add(def); - } -}; diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/init-modules.ts b/libs/ng-mocks/src/lib/mock-builder/promise/init-modules.ts index 608cea753e..4f9adabb9a 100644 --- a/libs/ng-mocks/src/lib/mock-builder/promise/init-modules.ts +++ b/libs/ng-mocks/src/lib/mock-builder/promise/init-modules.ts @@ -3,6 +3,7 @@ import { isNgDef } from '../../common/func.is-ng-def'; import ngMocksUniverse from '../../common/ng-mocks-universe'; import { MockModule } from '../../mock-module/mock-module'; import mockNgDef from '../../mock-module/mock-ng-def'; +import collectDeclarations from '../../resolve/collect-declarations'; export default ( keepDef: Set, @@ -13,21 +14,31 @@ export default ( const loProviders = new Map(); for (const def of [...mapValues(keepDef), ...mapValues(mockDef), ...mapValues(replaceDef)]) { - if (!isNgDef(def, 'm')) { - continue; + const meta = collectDeclarations(def); + const providers = [ + ...(defProviders.get(def) ?? []), + ...(meta.Component?.providers ?? []), + ...(meta.Directive?.providers ?? []), + ]; + + const deleteTouch = !ngMocksUniverse.touches.has(def); + if (!mockDef.has(def)) { + ngMocksUniverse.flags.add('skipMock'); } - if (defProviders.has(def)) { - if (!mockDef.has(def)) { - ngMocksUniverse.flags.add('skipMock'); - } - const [, loDef] = mockNgDef({ providers: defProviders.get(def) }); + const isModule = isNgDef(def, 'm'); + if (providers.length > 0) { + const [, loDef] = mockNgDef({ providers, skipMarkProviders: !isModule }); loProviders.set(def, loDef.providers); - ngMocksUniverse.flags.delete('skipMock'); + } + if (isModule) { + ngMocksUniverse.builtDeclarations.set(def, MockModule(def)); } - ngMocksUniverse.builtDeclarations.set(def, MockModule(def)); - ngMocksUniverse.touches.delete(def); + ngMocksUniverse.flags.delete('skipMock'); + if (deleteTouch) { + ngMocksUniverse.touches.delete(def); + } } return loProviders; 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 eb85a8abc0..938c5ea037 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 @@ -2,12 +2,14 @@ import { flatten, mapValues } from '../../common/core.helpers'; 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 initModule from './init-module'; -import skipInitModule from './skip-init-module'; import { BuilderData, NgMeta } from './types'; -const handleDef = ({ imports, declarations }: NgMeta, def: any, defProviders: Map): void => { +const handleDef = ({ imports, declarations, providers }: NgMeta, def: any, defProviders: Map): void => { + let touched = false; + if (isNgDef(def, 'm')) { const extendedDef = initModule(def, defProviders); imports.push(extendedDef); @@ -18,11 +20,25 @@ const handleDef = ({ imports, declarations }: NgMeta, def: any, defProviders: Ma ngMocksUniverse.touches.add(funcGetProvider(provider)); } } - } else { + touched = true; + } + + if (isNgDef(def, 'c') || isNgDef(def, 'd') || isNgDef(def, 'p')) { declarations.push(ngMocksUniverse.getBuildDeclaration(def)); + touched = true; + } + + if (isNgDef(def, 'i') || isNgDef(def, 't') || typeof def === 'string') { + const mock = ngMocksUniverse.builtProviders.get(def); + if (mock && typeof mock !== 'string' && isNgDef(mock, 't') === false) { + providers.push(mock); + } + touched = true; } - ngMocksUniverse.touches.add(def); + if (touched) { + ngMocksUniverse.touches.add(def); + } }; export default ({ configDef, keepDef, mockDef, replaceDef }: BuilderData, defProviders: Map): NgMeta => { @@ -30,10 +46,15 @@ export default ({ configDef, keepDef, mockDef, replaceDef }: BuilderData, defPro // Adding suitable leftovers. for (const def of [...mapValues(mockDef), ...mapValues(keepDef), ...mapValues(replaceDef)]) { - if (skipInitModule(def, configDef)) { - continue; + const configInstance = ngMocksUniverse.configInstance.get(def); + const config = configDef.get(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) { + handleDef(meta, def, defProviders); } - handleDef(meta, def, defProviders); } return meta; diff --git a/libs/ng-mocks/src/lib/mock-builder/promise/skip-init-module.ts b/libs/ng-mocks/src/lib/mock-builder/promise/skip-init-module.ts deleted file mode 100644 index 30b3652497..0000000000 --- a/libs/ng-mocks/src/lib/mock-builder/promise/skip-init-module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { isNgDef } from '../../common/func.is-ng-def'; -import ngMocksUniverse from '../../common/ng-mocks-universe'; - -import { BuilderData } from './types'; - -export default (def: any, configDef: BuilderData['configDef']): boolean => { - if (isNgDef(def, 'i') || !isNgDef(def)) { - return true; - } - if (ngMocksUniverse.touches.has(def)) { - return true; - } - - const config = configDef.get(def); - - return config && config.dependency; -}; diff --git a/libs/ng-mocks/src/lib/mock-module/mock-ng-def.ts b/libs/ng-mocks/src/lib/mock-module/mock-ng-def.ts index 3c0255d5ac..a0a8a26785 100644 --- a/libs/ng-mocks/src/lib/mock-module/mock-ng-def.ts +++ b/libs/ng-mocks/src/lib/mock-module/mock-ng-def.ts @@ -36,7 +36,9 @@ const configureProcessMetaKeys = ( ]; const processMeta = ( - ngModule: Partial, + ngModule: Partial & { + skipMarkProviders?: boolean; + }, resolve: (def: any) => any, resolveProvider: (def: Provider) => any, ): NgModule => { @@ -52,8 +54,10 @@ const processMeta = ( mockModuleDef[key] = flatToExisting(ngModule[key], callback); } } - markProviders(mockModuleDef.providers); - markProviders(mockModuleDef.viewProviders); + if (!ngModule.skipMarkProviders) { + markProviders(mockModuleDef.providers); + markProviders(mockModuleDef.viewProviders); + } if (!cachePipe) { ngMocksUniverse.flags.delete('cachePipe'); @@ -116,7 +120,12 @@ const addExports = ( } }; -export default (ngModuleDef: NgModule, ngModule?: Type): [boolean, NgModule] => { +export default ( + ngModuleDef: NgModule & { + skipMarkProviders?: boolean; + }, + ngModule?: Type, +): [boolean, NgModule] => { const hasResolver = ngMocksUniverse.config.has('mockNgDefResolver'); if (!hasResolver) { ngMocksUniverse.config.set('mockNgDefResolver', new Map()); diff --git a/tests/issue-2646/test.spec.ts b/tests/issue-2646/test.spec.ts new file mode 100644 index 0000000000..415d292186 --- /dev/null +++ b/tests/issue-2646/test.spec.ts @@ -0,0 +1,267 @@ +import { + Component, + Directive, + Injectable, + NgModule, + Pipe, + PipeTransform, +} from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + +import { isMockOf, MockBuilder, MockRender, ngMocks } from 'ng-mocks'; + +@Injectable() +class TargetService { + echo() { + return `TargetService`; + } +} + +@Directive({ + selector: 'target', +}) +class TargetDirective { + echo() { + return `TargetDirective`; + } +} + +@Component({ + selector: 'target', + template: '{{ echo() | target }}', +}) +class TargetComponent { + echo() { + return `TargetComponent`; + } +} + +// @see https://github.com/ike18t/ng-mocks/issues/2646 +// MockBuilder should build a module correctly based on all providers, +// even if declarations derive from services +describe('issue-2646', () => { + describe('directive', () => { + @Directive({ + selector: 'target', + }) + class ServiceToDirective extends TargetService { + echo() { + return `ServiceToDirective`; + } + } + + describe('real', () => { + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [ServiceToDirective], + providers: [ServiceToDirective], + }).compileComponents(), + ); + + it('covers behavior', () => { + const fixture = MockRender(ServiceToDirective); + expect(fixture.point.componentInstance.constructor).toBe( + ServiceToDirective, + ); + + expect(() => + fixture.debugElement.injector.get(ServiceToDirective), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + + describe('mock', () => { + beforeEach(() => MockBuilder().mock(ServiceToDirective)); + + it('covers behavior', () => { + const fixture = MockRender(ServiceToDirective); + expect( + isMockOf( + fixture.point.componentInstance, + ServiceToDirective, + ), + ).toEqual(true); + + expect(() => + fixture.debugElement.injector.get(ServiceToDirective), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + }); + + describe('component', () => { + @Component({ + selector: 'target', + template: 'target', + }) + class ServiceToComponent extends TargetService { + echo() { + return `ServiceToComponent`; + } + } + + describe('real', () => { + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [ServiceToComponent], + providers: [ServiceToComponent], + }).compileComponents(), + ); + + it('covers behavior', () => { + const fixture = MockRender(ServiceToComponent); + expect(fixture.point.componentInstance.constructor).toBe( + ServiceToComponent, + ); + + expect(() => + fixture.debugElement.injector.get(ServiceToComponent), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + + describe('mock', () => { + beforeEach(() => MockBuilder().mock(ServiceToComponent)); + + it('covers behavior', () => { + const fixture = MockRender(ServiceToComponent); + expect( + isMockOf( + fixture.point.componentInstance, + ServiceToComponent, + ), + ).toEqual(true); + + expect(() => + fixture.debugElement.injector.get(ServiceToComponent), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + }); + + describe('pipe', () => { + @Pipe({ + name: 'target', + }) + class PipeFromService + extends TargetService + implements PipeTransform + { + transform(): string { + return 'PipeFromService'; + } + } + + describe('real', () => { + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [PipeFromService, TargetComponent], + providers: [PipeFromService], + }).compileComponents(), + ); + + it('covers behavior', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.point.componentInstance.constructor).toBe( + TargetComponent, + ); + expect(ngMocks.formatText(fixture)).toEqual( + 'PipeFromService', + ); + + expect(() => + fixture.debugElement.injector.get(PipeFromService), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + + describe('mock', () => { + beforeEach(() => + MockBuilder(TargetComponent).mock(PipeFromService), + ); + + it('covers behavior', () => { + const fixture = MockRender(TargetComponent); + expect(fixture.point.componentInstance.constructor).toBe( + TargetComponent, + ); + expect(ngMocks.formatText(fixture)).toEqual(''); + + expect(() => + fixture.debugElement.injector.get(PipeFromService), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + }); + + describe('module', () => { + @NgModule({ + declarations: [TargetDirective], + exports: [TargetDirective], + }) + class ModuleFromService extends TargetService { + echo() { + return 'ModuleFromService'; + } + } + + describe('real', () => { + beforeEach(() => + TestBed.configureTestingModule({ + imports: [ModuleFromService], + providers: [ModuleFromService], + }).compileComponents(), + ); + + it('covers behavior', () => { + const fixture = MockRender(TargetDirective); + expect(fixture.point.componentInstance.constructor).toBe( + TargetDirective, + ); + + expect(() => + fixture.debugElement.injector.get(ModuleFromService), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + + describe('mock', () => { + beforeEach(() => MockBuilder().mock(ModuleFromService)); + + it('covers behavior', () => { + const fixture = MockRender(TargetDirective); + expect( + isMockOf(fixture.point.componentInstance, TargetDirective), + ).toEqual(true); + + expect(() => + fixture.debugElement.injector.get(ModuleFromService), + ).not.toThrow(); + expect(() => + fixture.debugElement.injector.get(TargetService), + ).toThrowError(/No provider/); + }); + }); + }); +});