UNPKG

@nestjs/testing

Version:

Nest - modern, fast, powerful node.js web framework (@testing)

80 lines (79 loc) • 3.33 kB
import { STATIC_CONTEXT, Injector, InstanceWrapper, } from '@nestjs/core/internal'; /** * @publicApi */ export class TestingInjector extends Injector { mocker; container; mockedInstances = new Map(); setMocker(mocker) { this.mocker = mocker; this.mockedInstances.clear(); } setContainer(container) { this.container = container; } // Request-scoped and transient providers are not loaded at compile time, // and the injector that resolves them later has no mocker. Their // dependencies are still looked up here, through resolveComponentWrapper(), // which mocks the missing ones without creating any instance. async lookupSkippedProviderDependencies(wrapper, moduleRef) { const lookup = super.lookupSkippedProviderDependencies(wrapper, moduleRef); // With a mocker, a dependency that cannot be mocked fails later, at // resolve time. return this.mocker ? lookup.catch(() => undefined) : lookup; } async resolveComponentWrapper(moduleRef, name, dependencyContext, wrapper, resolutionContext = { contextId: STATIC_CONTEXT }, keyOrIndex) { try { const existingProviderWrapper = await super.resolveComponentWrapper(moduleRef, name, dependencyContext, wrapper, resolutionContext, keyOrIndex); return existingProviderWrapper; } catch (err) { return this.mockWrapper(err, moduleRef, name, name, wrapper); } } async resolveComponentHost(moduleRef, instanceWrapper, resolutionContext = { contextId: STATIC_CONTEXT }) { try { const existingProviderWrapper = await super.resolveComponentHost(moduleRef, instanceWrapper, resolutionContext); return existingProviderWrapper; } catch (err) { return this.mockWrapper(err, moduleRef, instanceWrapper.name, instanceWrapper.token, instanceWrapper); } } async mockWrapper(err, moduleRef, name, token, wrapper) { if (!this.mocker) { throw err; } // Every provider that depends on the token must get the same mock, // the one `get()` returns. let mockedInstance = this.mockedInstances.get(token); if (!mockedInstance) { mockedInstance = this.mocker(name); if (!mockedInstance) { throw err; } this.mockedInstances.set(token, mockedInstance); } // The mock is a single value, so it stays static whatever the scope of // the provider it replaces or the provider that depends on it. const newWrapper = new InstanceWrapper({ name, isAlias: false, instance: mockedInstance, isResolved: true, host: moduleRef, metatype: wrapper.metatype, }); const internalCoreModule = this.container.getInternalCoreModuleRef(); if (!internalCoreModule) { throw new Error('Expected to have internal core module reference at this point.'); } internalCoreModule.addCustomProvider({ provide: name, useValue: mockedInstance, }, internalCoreModule.providers); internalCoreModule.addExportedProviderOrModule(name); return newWrapper; } }