Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why does angular 2 provider {useValue: ...} clone objects?

Angular 2 advanced testing doc:

userServiceStub = {
  isLoggedIn: true,
  user: { name: 'Test User'}
};

TestBed.configureTestingModule({
   declarations: [ WelcomeComponent ],
// providers:    [ UserService ]  // NO! Don't provide the real service!
                                  // Provide a test-double instead
   providers:    [ {provide: UserService, useValue: userServiceStub } ]
});

fixture = TestBed.createComponent(WelcomeComponent);
comp    = fixture.componentInstance;

// UserService actually injected into the component
userService = fixture.debugElement.injector.get(UserService);
componentUserService = userService;
// UserService from the root injector
userService = TestBed.get(UserService);

It seems that the service, userService form injector.get() and userServiceStub from we explicitly created are different Object.

it('stub object and injected UserService should not be the same', () => {
  expect(userServiceStub === userService).toBe(false);

  // Changing the stub object has no effect on the injected service
  userServiceStub.isLoggedIn = false;
  expect(userService.isLoggedIn).toBe(true);
});

The doc say that the userService here is a copy of userServiceStub. Is that means that Angular 2 DI treat all {provide: someting, useValue: someValue} as clone a new Object frome someValue? Why DI system not use someValue directly?

Related github issue of Angular.io.

like image 211
maxisacoder Avatar asked Nov 30 '16 07:11

maxisacoder


1 Answers

Angular2 DI maintains a single instance per provider. If a key (type, string or OpaqueToken) is provided multiple times you'll get multiple instances.

In your example UserService is provided by the testing module and DI will always return the same instance from this module, but if there is a provider with the same key closer to where the dependency is request (for example on the component itself, then an instance from this provider will be injected.

To override the behavior for the test there are different way, depending on the exact use case.

One example:

TestBed.overrideComponent(comp, {set /* or add */: 
    {providers: [/* new providers here */]}
});

Update

There is a related open issue https://github.com/angular/angular/issues/10788 Seems this actually doesn't work as expected.

like image 133
Günter Zöchbauer Avatar answered Oct 24 '22 09:10

Günter Zöchbauer