Major bug, request scope in a singleton class is not working
request-scoped dependency is not working in a singleton class.
Expected Behavior
Writing a test case to validate the behavior of a singleton class with a request-scoped dependency involves simulating different request contexts and verifying that the singleton class behaves according to the request-scoped dependency. Here's a basic example using a hypothetical Node.js environment with Inversify for dependency injection and a testing framework like Jest.
Current Behavior
Request scope dependency is not refreshing with new value.
Steps to Reproduce (for bugs)
Singleton Service: MySingletonService is a singleton and should be the same instance in both contexts. Request-Scoped Dependency: MyRequestScopedDependency is request-scoped, so it should have different values in different contexts.
import { Container, inject, injectable } from 'inversify';
import 'reflect-metadata';
import { Context } from 'inversify/lib/planning/context';
const TYPES = {
MyRequestScopedDependency: Symbol.for('MyRequestScopedDependency'),
MySingletonService: Symbol.for('MySingletonService'),
};
@injectable()
class MyRequestScopedDependency {
public value: number;
constructor() {
this.value = Math.random();
}
}
@injectable()
class MySingletonService {
private requestScopedDependency: MyRequestScopedDependency;
constructor(@inject(TYPES.MyRequestScopedDependency) requestScopedDependency: MyRequestScopedDependency) {
this.requestScopedDependency = requestScopedDependency;
}
getRequestScopedValue() {
return this.requestScopedDependency.value;
}
}
const container = new Container();
container
.bind<MyRequestScopedDependency>(TYPES.MyRequestScopedDependency)
.to(MyRequestScopedDependency)
.inRequestScope();
container.bind<MySingletonService>(TYPES.MySingletonService).to(MySingletonService).inSingletonScope();
describe('Singleton with Request-Scoped Dependency', () => {
it('should have different request-scoped dependencies in different contexts', () => {
// Simulate two different requests
const request1Context = new Context(container);
const request2Context = new Context(container);
const singletonService1 = request1Context.container.get<MySingletonService>(TYPES.MySingletonService);
const singletonService2 = request2Context.container.get<MySingletonService>(TYPES.MySingletonService);
// Both instances should be the same (singleton)
expect(singletonService1).toBe(singletonService2);
console.log('VALUE__', singletonService1.getRequestScopedValue(), singletonService2.getRequestScopedValue() )
// But their request-scoped dependencies should be different <----THIS TEST FAILS
expect(singletonService1.getRequestScopedValue()).not.toBe(singletonService2.getRequestScopedValue());
});
});
Your Environment
- Version used: 6.0.2
- Environment name and version (e.g. Chrome 39, node.js 5.4): nodjes 18
- Operating System and version (desktop or mobile): Macos
This might be my workaround
const TYPES = {
RequestScopedDependencyFactory: Symbol.for('RequestScopedDependencyFactory'),
MyRequestScopedDependency: Symbol.for('MyRequestScopedDependency'),
MySingletonService: Symbol.for('MySingletonService'),
};
@injectable()
class MyRequestScopedDependency {
public value: number;
constructor() {
this.value = Math.random();
}
}
@injectable()
class MySingletonService {
private getRequestScopedDependency: () => MyRequestScopedDependency;
constructor(
@inject(TYPES.RequestScopedDependencyFactory)
getRequestScopedDependencyFactory: () => MyRequestScopedDependency,
) {
this.getRequestScopedDependency = getRequestScopedDependencyFactory;
}
getRequestScopedValue() {
return this.getRequestScopedDependency().value;
}
}
const container = new Container();
container
.bind<() => MyRequestScopedDependency>(TYPES.RequestScopedDependencyFactory)
.toFactory<MyRequestScopedDependency>(
(context: interfaces.Context) => () =>
context.container.get<MyRequestScopedDependency>(TYPES.MyRequestScopedDependency),
);
container
.bind<MyRequestScopedDependency>(TYPES.MyRequestScopedDependency)
.to(MyRequestScopedDependency)
.inRequestScope();
container.bind<MySingletonService>(TYPES.MySingletonService).to(MySingletonService).inSingletonScope();