-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Angular CT providers are not added to the TestingModule #23427
Comments
Until this bug is fixed, for those of you that stumble upon this. There's a workaround: Create and add a dedicated "YourXyzTestingModule" to your |
Hi @NicBright , we originally had the implementation you're asking for but we reverted it based on this issue. The component under test may need a custom implementation of a given provider but other uses rendered in that test may need another, and separating the providers in this way rather than forcing them to the TestBed level makes that a bit easier. For situations where you want a singleton provider your identified workaround (nice job finding that, btw) would be a solution. Of course, that may not be obvious to many users so this may be useful to note in our angular documentation |
@NicBright I am unable to duplicate the issue you are describing. I created an example of what you are describing here. Like @mike-plummer mentioned we are purposely overriding the component providers at the component level |
@mike-plummer
That's totally true! This could be addressed via #23425 @jordanpowell88 To summarize:
|
@NicBright I am still not able to reproduce the issue you are describing. Are you able to provide me a link to a repo? I just created the following test and it is working the way you are describing it would not:
|
The following example reproduces the two issues: I've created an additional child component named @Component({
template: `
<h1>Parent Component</h1>
<app-child-providers></app-child-providers>
`,
})
export class ParentProvidersComponent {}
@Injectable({
providedIn: 'root',
})
export class ChildProvidersService {
constructor(private readonly http: HttpClient) {}
getData(): Observable<string> {
return this.http.get<string>('https://myfakeapiurl.com/api/data');
}
}
@Component({
selector: 'app-child-providers',
template: `<button (click)="handleClick()">{{ message }}</button>`,
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class ChildProvidersComponent {
message = 'default message';
constructor(private readonly service: ChildProvidersService) {}
handleClick(): void {
this.service
.getData()
.pipe(take(1))
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
.subscribe((value) => {
this.message = value;
});
}
}
@Component({
selector: 'app-child-providers',
template: `<button (click)="handleClick()">{{ message }}</button>`,
providers: [ChildProvidersService], // such a provider cannot be replaced with a double!
})
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
export class AnotherChildProvidersComponent {
message = 'default message';
constructor(private readonly service: ChildProvidersService) {}
handleClick(): void {
this.service
.getData()
.pipe(take(1))
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
.subscribe((value) => {
this.message = value;
});
}
}
describe('Cypress issue 23427', () => {
it('This works - it seems that providers added to a component are also available for child components', () => {
cy.mount(ParentProvidersComponent, {
declarations: [ChildProvidersComponent],
imports: [],
providers: [
{
provide: ChildProvidersService,
useValue: {
getData() {
return of('test');
},
} as ChildProvidersService,
},
],
});
cy.contains('default message').click();
cy.contains('test');
});
it('However they are NOT available for child components when they have dedicated @Component({providers}) configured (this test will fail)', () => {
cy.mount(ParentProvidersComponent, {
declarations: [AnotherChildProvidersComponent],
imports: [HttpClientTestingModule],
providers: [
{
provide: ChildProvidersService,
useValue: {
getData() {
return of('test');
},
} as ChildProvidersService,
},
],
});
cy.contains('default message').click();
cy.contains('test');
});
it('You also cannot provide a double for a dependency of a singleton service (this test will fail)', () => {
cy.mount(ParentProvidersComponent, {
declarations: [ChildProvidersComponent],
imports: [],
providers: [
{ // try to provide a double for ChildProvidersService's dependency to HttpClient:
provide: HttpClient,
useValue: {
get() {
return of('test');
},
},
},
],
});
cy.contains('default message').click();
cy.contains('test');
});
}); |
Hey Nic. Thanks again for providing the thorough example of your problem. The best way to accomplish what you are wanting to do at the moment is to use something like the following before your
You still have access to the same Static |
Going to reopen, as |
This is solved in #24394 |
Released in This comment thread has been locked. If you are still experiencing this issue after upgrading to |
Current behavior
The bug is obvious:
cypress/npm/angular/src/mount.ts
Lines 143 to 157 in d201b37
The providers that are meant to be added to the TestingModule are instead only added in a call to
TestBed.overrideComponent()
. This way, it is not possible to alter dependencies of singleton services aka services that are{ providedIn: 'root' }
(or any custom module).Desired behavior
Passed providers must be passed to
TestBed.configureTestingModule()
Test code to reproduce
Irrelevant. See source code snippet above.
Cypress Version
10.6.0
Node version
irrelevant
Operating System
irrelevant
Debug Logs
No response
Other
No response
The text was updated successfully, but these errors were encountered: