diff --git a/src/container/container.ts b/src/container/container.ts index 126f1ac..dddd68d 100644 --- a/src/container/container.ts +++ b/src/container/container.ts @@ -7,6 +7,7 @@ export class Container { public readonly id: ContainerIdentifier; private metadataMap: Map = new Map(); + private bindingMap: Map = new Map(); private resolving = new Set(); private resolvingPath: ServiceIdentifier[] = []; @@ -30,11 +31,11 @@ export class Container { return container; } - public set(metadata: Metadata) { + public register(metadata: Metadata) { if (metadata.scope === 'singleton' && !this.isDefault()) { - ContainerRegistry.defaultContainer.set(metadata); + ContainerRegistry.defaultContainer.register(metadata); this.metadataMap.delete(metadata.id); - return; + return this; } const newMetadata: Metadata = { @@ -48,23 +49,45 @@ export class Container { } else { this.metadataMap.set(newMetadata.id, newMetadata); } + + return this; } public has(id: ServiceIdentifier): boolean { return this.metadataMap.has(id); } + public set(id: ServiceIdentifier, value: T) { + this.bindingMap.set(id, value); + return this; + } + + public remove(id: ServiceIdentifier) { + this.bindingMap.delete(id); + this.metadataMap.delete(id); + return this; + } + public reset(strategy: 'value' | 'service' = 'value') { if (strategy === 'value') { this.metadataMap.forEach((metadata) => { metadata.value = EMPTY_VALUE; }); + + return this; } else { + this.bindingMap.clear(); this.metadataMap.clear(); + + return this; } } public get(id: ServiceIdentifier): T { + if (this.bindingMap.has(id)) { + return this.bindingMap.get(id) as T; + } + let metadata = this.metadataMap.get(id); if (!metadata && !this.isDefault()) { @@ -131,4 +154,10 @@ export class Container { private isDefault() { return this === ContainerRegistry.defaultContainer; } + + public static reset(containerId: ContainerIdentifier, options?: { strategy?: 'value' | 'service' }) { + const container = ContainerRegistry.getContainer(containerId); + + container?.reset(options?.strategy); + } } diff --git a/src/decorators/service.ts b/src/decorators/service.ts index 0ed207e..c3c62a1 100644 --- a/src/decorators/service.ts +++ b/src/decorators/service.ts @@ -23,7 +23,7 @@ export function Service(idOrOptions?: ServiceIdentifier | ServiceOption) { const injections = (context.metadata[INJECTION_KEY] ?? []) as InjectionMetadata[]; - ContainerRegistry.defaultContainer.set({ + ContainerRegistry.defaultContainer.register({ id: options?.id ?? target, Class: target, name: context.name, diff --git a/test/container/container.test.ts b/test/container/container.test.ts index 8f8ac19..df78429 100644 --- a/test/container/container.test.ts +++ b/test/container/container.test.ts @@ -6,10 +6,13 @@ import { EMPTY_VALUE } from '../../src/types'; afterEach(() => { Container.of().reset('service'); + ContainerRegistry.removeContainer('container'); ContainerRegistry.removeContainer('container-reused'); ContainerRegistry.removeContainer('container-first'); ContainerRegistry.removeContainer('container-second'); ContainerRegistry.removeContainer('container-scope'); + ContainerRegistry.removeContainer('container-binding'); + ContainerRegistry.removeContainer('container-binding-reset'); ContainerRegistry.removeContainer('singleton-forwarded'); ContainerRegistry.removeContainer('container-reset-fallback'); ContainerRegistry.removeContainer('container-local-has'); @@ -17,224 +20,330 @@ afterEach(() => { }); describe('Container', () => { - test('returns the default container for no id and the default id', () => { - expect(Container.of()).toBe(Container.of('default')); - }); + describe('of', () => { + test('returns the default container for no id and the default id', () => { + expect(Container.of()).toBe(Container.of('default')); + }); - test('reuses named containers for the same id', () => { - expect(Container.of('container-reused')).toBe(Container.of('container-reused')); - }); + test('reuses named containers for the same id', () => { + expect(Container.of('container-reused')).toBe(Container.of('container-reused')); + }); - test('creates distinct named containers for different ids', () => { - expect(Container.of('container-first')).not.toBe(Container.of('container-second')); + test('creates distinct named containers for different ids', () => { + expect(Container.of('container-first')).not.toBe(Container.of('container-second')); + }); }); - test('reuses container-scoped services within the same container and isolates them across containers', () => { - const requestContainer = Container.of('container-scope'); + describe('register', () => { + test('reuses container-scoped services within the same container and isolates them across containers', () => { + const requestContainer = Container.of('container-scope'); + + class RequestService {} + + Container.of().register({ + id: RequestService, + Class: RequestService, + name: 'RequestService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); + + const defaultFirst = Container.of().get(RequestService); + const defaultSecond = Container.of().get(RequestService); + const requestFirst = requestContainer.get(RequestService); + const requestSecond = requestContainer.get(RequestService); + + expect(defaultFirst).toBe(defaultSecond); + expect(requestFirst).toBe(requestSecond); + expect(defaultFirst).not.toBe(requestFirst); + }); - class RequestService {} + test('stores singleton registrations in the default container when set from a named container', () => { + const requestContainer = Container.of('singleton-forwarded'); - Container.of().set({ - id: RequestService, - Class: RequestService, - name: 'RequestService', - injections: [], - scope: 'container', - value: EMPTY_VALUE, - }); + class SingletonService {} - const defaultFirst = Container.of().get(RequestService); - const defaultSecond = Container.of().get(RequestService); - const requestFirst = requestContainer.get(RequestService); - const requestSecond = requestContainer.get(RequestService); + requestContainer.register({ + id: SingletonService, + Class: SingletonService, + name: 'SingletonService', + injections: [], + scope: 'singleton', + value: EMPTY_VALUE, + }); - expect(defaultFirst).toBe(defaultSecond); - expect(requestFirst).toBe(requestSecond); - expect(defaultFirst).not.toBe(requestFirst); + expect(requestContainer.has(SingletonService)).toBe(false); + expect(Container.of().has(SingletonService)).toBe(true); + expect(requestContainer.get(SingletonService)).toBe(Container.of().get(SingletonService)); + }); }); - test('stores singleton registrations in the default container when set from a named container', () => { - const requestContainer = Container.of('singleton-forwarded'); + describe('set', () => { + test('returns a bound value set on the current container', () => { + class BoundService {} - class SingletonService {} + const bound = new BoundService(); - requestContainer.set({ - id: SingletonService, - Class: SingletonService, - name: 'SingletonService', - injections: [], - scope: 'singleton', - value: EMPTY_VALUE, + Container.of().set(BoundService, bound); + + expect(Container.of().get(BoundService)).toBe(bound); }); - expect(requestContainer.has(SingletonService)).toBe(false); - expect(Container.of().has(SingletonService)).toBe(true); - expect(requestContainer.get(SingletonService)).toBe(Container.of().get(SingletonService)); - }); + test('prefers a bound value over a registered service in the same container', () => { + class BoundService {} - test('reset value clears cached instances but keeps registrations', () => { - class ResettableService {} + const bound = new BoundService(); - Container.of().set({ - id: ResettableService, - Class: ResettableService, - name: 'ResettableService', - injections: [], - scope: 'container', - value: EMPTY_VALUE, + Container.of().register({ + id: BoundService, + Class: BoundService, + name: 'BoundService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); + + Container.of().set(BoundService, bound); + + expect(Container.of().get(BoundService)).toBe(bound); }); + }); - const beforeReset = Container.of().get(ResettableService); + describe('remove', () => { + test('clears a bound value from the current container', () => { + const requestContainer = Container.of('container-binding'); - Container.of().reset('value'); + class BoundService {} - const afterReset = Container.of().get(ResettableService); + const bound = new BoundService(); - expect(afterReset).toBeInstanceOf(ResettableService); - expect(afterReset).not.toBe(beforeReset); - }); + requestContainer.set(BoundService, bound); + + expect(requestContainer.get(BoundService)).toBe(bound); - test('reset service removes registrations from the current container', () => { - class ResettableService {} + requestContainer.remove(BoundService); - Container.of().set({ - id: ResettableService, - Class: ResettableService, - name: 'ResettableService', - injections: [], - scope: 'container', - value: EMPTY_VALUE, + expect(() => requestContainer.get(BoundService)).toThrow(ServiceNotFoundError); }); + }); - Container.of().reset('service'); + describe('reset', () => { + describe("'value'", () => { + test('clears cached instances but keeps registrations', () => { + class ResettableService {} - expect(() => Container.of().get(ResettableService)).toThrow(ServiceNotFoundError); - }); + Container.of().register({ + id: ResettableService, + Class: ResettableService, + name: 'ResettableService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); + + const beforeReset = Container.of().get(ResettableService); + + Container.of().reset('value'); - test('reset service on a named container clears local copies and falls back to default registrations again', () => { - const requestContainer = Container.of('container-reset-fallback'); + const afterReset = Container.of().get(ResettableService); - class ResettableService {} + expect(afterReset).toBeInstanceOf(ResettableService); + expect(afterReset).not.toBe(beforeReset); + }); - Container.of().set({ - id: ResettableService, - Class: ResettableService, - name: 'ResettableService', - injections: [], - scope: 'container', - value: EMPTY_VALUE, + test('keeps bound values intact', () => { + class BoundService {} + + const bound = new BoundService(); + + Container.of().set(BoundService, bound); + Container.of().reset('value'); + + expect(Container.of().get(BoundService)).toBe(bound); + }); }); - const first = requestContainer.get(ResettableService); + describe("'service'", () => { + test('removes registrations from the current container', () => { + class ResettableService {} - requestContainer.reset('service'); + Container.of().register({ + id: ResettableService, + Class: ResettableService, + name: 'ResettableService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); - const second = requestContainer.get(ResettableService); + Container.of().reset('service'); - expect(second).toBeInstanceOf(ResettableService); - expect(second).not.toBe(first); - expect(second).not.toBe(Container.of().get(ResettableService)); - }); + expect(() => Container.of().get(ResettableService)).toThrow(ServiceNotFoundError); + }); - test('has only reports local registrations', () => { - const requestContainer = Container.of('container-local-has'); + test('removes bound values from the current container', () => { + const requestContainer = Container.of('container-binding-reset'); - class ScopedService {} + class BoundService {} - Container.of().set({ - id: ScopedService, - Class: ScopedService, - name: 'ScopedService', - injections: [], - scope: 'container', - value: EMPTY_VALUE, - }); + const bound = new BoundService(); - expect(Container.of().has(ScopedService)).toBe(true); - expect(requestContainer.has(ScopedService)).toBe(false); + requestContainer.set(BoundService, bound); + requestContainer.reset('service'); - requestContainer.get(ScopedService); + expect(() => requestContainer.get(BoundService)).toThrow(ServiceNotFoundError); + }); - expect(requestContainer.has(ScopedService)).toBe(true); - }); + test('on a named container clears local copies and falls back to default registrations again', () => { + const requestContainer = Container.of('container-reset-fallback'); - test('throws when a service is missing', () => { - class MissingService {} + class ResettableService {} - expect(() => Container.of().get(MissingService)).toThrow(ServiceNotFoundError); - }); + Container.of().register({ + id: ResettableService, + Class: ResettableService, + name: 'ResettableService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); - test('throws when dependencies form a circular graph', () => { - class AlphaService { - public beta!: BetaService; - } - - class BetaService { - public alpha!: AlphaService; - } - - Container.of().set({ - id: AlphaService, - Class: AlphaService, - name: 'AlphaService', - injections: [{ id: BetaService, name: 'beta' }], - scope: 'container', - value: EMPTY_VALUE, - }); + const first = requestContainer.get(ResettableService); + + requestContainer.reset('service'); - Container.of().set({ - id: BetaService, - Class: BetaService, - name: 'BetaService', - injections: [{ id: AlphaService, name: 'alpha' }], - scope: 'container', - value: EMPTY_VALUE, + const second = requestContainer.get(ResettableService); + + expect(second).toBeInstanceOf(ResettableService); + expect(second).not.toBe(first); + expect(second).not.toBe(Container.of().get(ResettableService)); + }); }); - expect(() => Container.of().get(AlphaService)).toThrow(CircularDependencyError); + describe('static reset', () => { + test('resets a specific container', () => { + class ResettableService {} + + Container.of('container').register({ + id: ResettableService, + Class: ResettableService, + name: 'ResettableService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); + + Container.reset('container', { strategy: 'service' }); + + expect(() => ContainerRegistry.getContainer('container')?.get(ResettableService)).toThrow(ServiceNotFoundError); + }); + }); }); - test('recovers cleanly after a circular resolution error', () => { - const requestContainer = Container.of('container-post-error'); + describe('has', () => { + test('only reports local registrations', () => { + const requestContainer = Container.of('container-local-has'); - class AlphaService { - public beta!: BetaService; - } + class ScopedService {} - class BetaService { - public alpha!: AlphaService; - } + Container.of().register({ + id: ScopedService, + Class: ScopedService, + name: 'ScopedService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); - class HealthyService {} + expect(Container.of().has(ScopedService)).toBe(true); + expect(requestContainer.has(ScopedService)).toBe(false); - requestContainer.set({ - id: AlphaService, - Class: AlphaService, - name: 'AlphaService', - injections: [{ id: BetaService, name: 'beta' }], - scope: 'container', - value: EMPTY_VALUE, + requestContainer.get(ScopedService); + + expect(requestContainer.has(ScopedService)).toBe(true); }); + }); - requestContainer.set({ - id: BetaService, - Class: BetaService, - name: 'BetaService', - injections: [{ id: AlphaService, name: 'alpha' }], - scope: 'container', - value: EMPTY_VALUE, + describe('get', () => { + test('throws when a service is missing', () => { + class MissingService {} + + expect(() => Container.of().get(MissingService)).toThrow(ServiceNotFoundError); }); - requestContainer.set({ - id: HealthyService, - Class: HealthyService, - name: 'HealthyService', - injections: [], - scope: 'container', - value: EMPTY_VALUE, + test('throws when dependencies form a circular graph', () => { + class AlphaService { + public beta!: BetaService; + } + + class BetaService { + public alpha!: AlphaService; + } + + Container.of().register({ + id: AlphaService, + Class: AlphaService, + name: 'AlphaService', + injections: [{ id: BetaService, name: 'beta' }], + scope: 'container', + value: EMPTY_VALUE, + }); + + Container.of().register({ + id: BetaService, + Class: BetaService, + name: 'BetaService', + injections: [{ id: AlphaService, name: 'alpha' }], + scope: 'container', + value: EMPTY_VALUE, + }); + + expect(() => Container.of().get(AlphaService)).toThrow(CircularDependencyError); }); - expect(() => requestContainer.get(AlphaService)).toThrow(CircularDependencyError); - expect(requestContainer.get(HealthyService)).toBeInstanceOf(HealthyService); + test('recovers cleanly after a circular resolution error', () => { + const requestContainer = Container.of('container-post-error'); + + class AlphaService { + public beta!: BetaService; + } + + class BetaService { + public alpha!: AlphaService; + } + + class HealthyService {} + + requestContainer.register({ + id: AlphaService, + Class: AlphaService, + name: 'AlphaService', + injections: [{ id: BetaService, name: 'beta' }], + scope: 'container', + value: EMPTY_VALUE, + }); + + requestContainer.register({ + id: BetaService, + Class: BetaService, + name: 'BetaService', + injections: [{ id: AlphaService, name: 'alpha' }], + scope: 'container', + value: EMPTY_VALUE, + }); + + requestContainer.register({ + id: HealthyService, + Class: HealthyService, + name: 'HealthyService', + injections: [], + scope: 'container', + value: EMPTY_VALUE, + }); + + expect(() => requestContainer.get(AlphaService)).toThrow(CircularDependencyError); + expect(requestContainer.get(HealthyService)).toBeInstanceOf(HealthyService); + }); }); }); diff --git a/test/container/registry.test.ts b/test/container/registry.test.ts index 86d8d95..0c56ecd 100644 --- a/test/container/registry.test.ts +++ b/test/container/registry.test.ts @@ -11,31 +11,37 @@ afterEach(() => { }); describe('ContainerRegistry', () => { - test('throws when registering another container with the same id', () => { - Container.of('registry-duplicate'); - - expect(() => ContainerRegistry.registerContainer(new Container('registry-duplicate'))).toThrow( - ContainerDuplicatedError, - ); + describe('registerContainer', () => { + test('throws when registering another container with the same id', () => { + Container.of('registry-duplicate'); + + expect(() => ContainerRegistry.registerContainer(new Container('registry-duplicate'))).toThrow( + ContainerDuplicatedError, + ); + }); }); - test('reports named container presence through hasContainer and getContainer', () => { - const container = Container.of('registry-visible'); + describe('hasContainer and getContainer', () => { + test('report named container presence', () => { + const container = Container.of('registry-visible'); - expect(ContainerRegistry.hasContainer('registry-visible')).toBe(true); - expect(ContainerRegistry.getContainer('registry-visible')).toBe(container); + expect(ContainerRegistry.hasContainer('registry-visible')).toBe(true); + expect(ContainerRegistry.getContainer('registry-visible')).toBe(container); + }); }); - test('removes a named container from the registry', () => { - Container.of('registry-removed'); + describe('removeContainer', () => { + test('removes a named container from the registry', () => { + Container.of('registry-removed'); - ContainerRegistry.removeContainer('registry-removed'); + ContainerRegistry.removeContainer('registry-removed'); - expect(ContainerRegistry.hasContainer('registry-removed')).toBe(false); - expect(ContainerRegistry.getContainer('registry-removed')).toBeUndefined(); - }); + expect(ContainerRegistry.hasContainer('registry-removed')).toBe(false); + expect(ContainerRegistry.getContainer('registry-removed')).toBeUndefined(); + }); - test('throws when removing the default container', () => { - expect(() => ContainerRegistry.removeContainer('default')).toThrow(DefaultContainerIdError); + test('throws when removing the default container', () => { + expect(() => ContainerRegistry.removeContainer('default')).toThrow(DefaultContainerIdError); + }); }); }); diff --git a/test/decorators/inject.test.ts b/test/decorators/inject.test.ts index 8170327..2bf32b5 100644 --- a/test/decorators/inject.test.ts +++ b/test/decorators/inject.test.ts @@ -7,85 +7,92 @@ afterEach(() => { ContainerRegistry.removeContainer('inject-named-container'); }); -describe('Inject Decorator', () => { - test('injects a decorated dependency into a decorated class field', () => { - @Service() - class LoggerService {} - - @Service() - class HandlerService { - @Inject(LoggerService) - public logger!: LoggerService; - } - - const handler = Container.of().get(HandlerService); - const descriptor = Object.getOwnPropertyDescriptor(handler, 'logger'); - - expect(handler.logger).toBeInstanceOf(LoggerService); - expect(handler.logger).toBe(Container.of().get(LoggerService)); - expect(descriptor).toMatchObject({ - configurable: true, - writable: true, - value: handler.logger, +describe('Inject decorator', () => { + describe('@Inject()', () => { + test('injects a decorated dependency into a decorated class field', () => { + @Service() + class LoggerService {} + + @Service() + class HandlerService { + @Inject(LoggerService) + public logger!: LoggerService; + } + + const handler = Container.of().get(HandlerService); + const descriptor = Object.getOwnPropertyDescriptor(handler, 'logger'); + + expect(handler.logger).toBeInstanceOf(LoggerService); + expect(handler.logger).toBe(Container.of().get(LoggerService)); + expect(descriptor).toMatchObject({ + configurable: true, + writable: true, + value: handler.logger, + }); }); - }); - test('injects multiple decorated dependencies on the same class', () => { - @Service() - class LoggerService {} + test('injects multiple decorated dependencies on the same class', () => { + @Service() + class LoggerService {} - @Service() - class MetricsService {} + @Service() + class MetricsService {} - @Service() - class HandlerService { - @Inject(LoggerService) - public logger!: LoggerService; + @Service() + class HandlerService { + @Inject(LoggerService) + public logger!: LoggerService; - @Inject(MetricsService) - public metrics!: MetricsService; - } + @Inject(MetricsService) + public metrics!: MetricsService; + } - const handler = Container.of().get(HandlerService); + const handler = Container.of().get(HandlerService); - expect(handler.logger).toBe(Container.of().get(LoggerService)); - expect(handler.metrics).toBe(Container.of().get(MetricsService)); - }); + expect(handler.logger).toBe(Container.of().get(LoggerService)); + expect(handler.metrics).toBe(Container.of().get(MetricsService)); + }); - test('uses the current named container when resolving injected dependencies', () => { - const requestContainer = Container.of('inject-named-container'); + test('uses the current named container when resolving injected dependencies', () => { + const requestContainer = Container.of('inject-named-container'); - @Service() - class LoggerService {} + @Service() + class LoggerService {} - @Service() - class HandlerService { - @Inject(LoggerService) - public logger!: LoggerService; - } + @Service() + class HandlerService { + @Inject(LoggerService) + public logger!: LoggerService; + } - const handler = requestContainer.get(HandlerService); + const handler = requestContainer.get(HandlerService); - expect(handler.logger).toBe(requestContainer.get(LoggerService)); - expect(handler.logger).not.toBe(Container.of().get(LoggerService)); - }); + expect(handler.logger).toBe(requestContainer.get(LoggerService)); + expect(handler.logger).not.toBe(Container.of().get(LoggerService)); + }); + + test('injects a token-identified dependency into a decorated class field', () => { + interface Logger { + log(message: string): void; + } - test('injects a token-identified dependency into a decorated class field', () => { - interface Logger { - log(message: string): void; - } - const LOGGER = new Token('Logger'); - @Service(LOGGER) - class ConsoleLogger implements Logger { - public log(_: string) {} - } - @Service() - class HandlerService { - @Inject(LOGGER) - public logger!: Logger; - } - const handler = Container.of().get(HandlerService); - expect(handler.logger).toBeInstanceOf(ConsoleLogger); - expect(handler.logger).toBe(Container.of().get(LOGGER)); + const LOGGER = new Token('Logger'); + + @Service(LOGGER) + class ConsoleLogger implements Logger { + public log(_: string) {} + } + + @Service() + class HandlerService { + @Inject(LOGGER) + public logger!: Logger; + } + + const handler = Container.of().get(HandlerService); + + expect(handler.logger).toBeInstanceOf(ConsoleLogger); + expect(handler.logger).toBe(Container.of().get(LOGGER)); + }); }); }); diff --git a/test/decorators/service.test.ts b/test/decorators/service.test.ts index 6735510..ba04712 100644 --- a/test/decorators/service.test.ts +++ b/test/decorators/service.test.ts @@ -8,38 +8,44 @@ afterEach(() => { ContainerRegistry.removeContainer('service-singleton-container'); }); -describe('Service Decorator', () => { - test('registers a decorated class in the default container', () => { - @Service() - class TestService {} - - expect(Container.of().get(TestService)).toBeInstanceOf(TestService); +describe('Service decorator', () => { + describe('@Service()', () => { + test('registers a decorated class in the default container', () => { + @Service() + class TestService {} + + expect(Container.of().get(TestService)).toBeInstanceOf(TestService); + }); }); - test('supports custom service identifiers', () => { - @Service({ id: 'custom-service' }) - class NamedService {} + describe('@Service({ id })', () => { + test('supports custom service identifiers', () => { + @Service({ id: 'custom-service' }) + class NamedService {} - expect(Container.of().get('custom-service')).toBeInstanceOf(NamedService); - expect(() => Container.of().get(NamedService)).toThrow(ServiceNotFoundError); + expect(Container.of().get('custom-service')).toBeInstanceOf(NamedService); + expect(() => Container.of().get(NamedService)).toThrow(ServiceNotFoundError); + }); }); - test('honors transient scope declared through the decorator', () => { - @Service({ scope: 'transient' }) - class TransientService {} + describe('@Service({ scope })', () => { + test('honors transient scope declared through the decorator', () => { + @Service({ scope: 'transient' }) + class TransientService {} - const first = Container.of().get(TransientService); - const second = Container.of().get(TransientService); + const first = Container.of().get(TransientService); + const second = Container.of().get(TransientService); - expect(first).not.toBe(second); - }); + expect(first).not.toBe(second); + }); - test('honors singleton scope declared through the decorator across containers', () => { - const requestContainer = Container.of('service-singleton-container'); + test('honors singleton scope declared through the decorator across containers', () => { + const requestContainer = Container.of('service-singleton-container'); - @Service({ scope: 'singleton' }) - class SingletonService {} + @Service({ scope: 'singleton' }) + class SingletonService {} - expect(Container.of().get(SingletonService)).toBe(requestContainer.get(SingletonService)); + expect(Container.of().get(SingletonService)).toBe(requestContainer.get(SingletonService)); + }); }); });