mnasyrov / ditox

Dependency injection for modular web applications
https://ditox.js.org
MIT License
92 stars 8 forks source link

Override value in child container #31

Closed nilscox closed 11 months ago

nilscox commented 11 months ago

I'm trying to override an injected value in a child container, but the value passed to the class constructor is not overridden. Here is a test case to demonstrate what I'm trying to achieve:

import { createContainer, injectableClass, token } from 'ditox';

class Test {
  constructor(public injectedValue: number) {}
}

it('test', () => {
  const container = createContainer();

  const TOKENS = {
    class: token<Test>('class'),
    value: token<number>('value'),
  };

  container.bindValue(TOKENS.value, 42);
  container.bindFactory(TOKENS.class, injectableClass(Test, TOKENS.value));

  const childContainer = createContainer(container);

  childContainer.bindValue(TOKENS.value, 51);

  expect(childContainer.get(TOKENS.value)).toEqual(51); // ok
  expect(childContainer.get(TOKENS.class)?.injectedValue).toEqual(51); // expected 51, got 42
});

Is this the expected behavior? I'm using this pattern in tests: I create a global container instance with production bindings, and I want to create a child instance in my tests to overrides some dependencies for testing purpose.

Thanks for making this library, I really enjoy using it!

mnasyrov commented 11 months ago

Hello, By default bindFactory() uses the scope "singleton" for the factory. This means the following.

According to the example, TOKENS.class is bound to the parent container, so its dependencies are resolved from the parent container.

The "scoped" scope option can be used to override dependencies:

The example can be fixed in two ways:

  1. Adding a "scoped" option to bindFactory() of the parent container.

    container.bindValue(TOKENS.value, 42);
    container.bindFactory(TOKENS.class, injectableClass(Test, TOKENS.value), {scope: "scoped"});
  2. Binding a second factory with the "scoped" option to the child container.

    childContainer.bindValue(TOKENS.value, 51);
    childContainer.bindFactory(TOKENS.class, injectableClass(Test, TOKENS.value), {scope:  "scoped"});