@nestjs/testing
Version:
Nest - modern, fast, powerful node.js web framework (@testing)
80 lines (79 loc) • 3.33 kB
JavaScript
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;
}
}