I have an Angular 15 application and I created a component that is basically the same as an Angular Material snackbar, however, I am having problems when creating unit tests with Jasmine, as it always reports a rendering error, as if there were a missing dependency or they were in the wrong rendering order, I did tests by creating several forms of mocks, but without success. Below is the service file import { ApplicationRef, ComponentRef, createComponent, Injectable } from "@angular/core"; import { SnackbarComponent } from "../../pages/common/snackbar/snackbar.component"; import { HttpErrorResponse } from "@angular/common/http"; @Injectable({ providedIn: 'root' }) export class SnackbarService { private snackbarRef: ComponentRef<SnackbarComponent> | null = null; private timeoutId: any; constructor( private appRef: ApplicationRef, ) { } showSnackbar( type: 'success' | 'error' | 'warning' | 'info', message: string | HttpErrorResponse, options: { vertical?: 'top' | 'bottom'; horizontal?: 'left' | 'center' | 'right'; button?: true | false } = { vertical: 'top', horizontal: 'right', }, duration: number = 10000 // dez segundos por enquanto ) { if (this.snackbarRef) { this.closeSnackbar() } this.snackbarRef = createComponent(SnackbarComponent, { environmentInjector: this.appRef.injector }) this.snackbarRef.instance.config = { type, message, options } this.appRef.attachView(this.snackbarRef.hostView) document.body.appendChild(this.snackbarRef.location.nativeElement); this.timeoutId = setTimeout(() => this.closeSnackbar(), duration); } closeSnackbar() { if (this.snackbarRef) { this.appRef.detachView(this.snackbarRef.hostView) this.snackbarRef.destroy() this.snackbarRef = null if(this.timeoutId){ clearTimeout(this.timeoutId); this.timeoutId = null } } } } Here is the service test file import { CommonModule } from '@angular/common'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { fakeAsync, TestBed, tick } from '@angular/core/testing'; import { ApplicationRef } from '@angular/core'; import { SnackbarService } from './snackbar.service'; import { SnackbarComponent, SnackbarConfig } from '../../pages/common/snackbar/snackbar.component'; import { HttpErrorResponse } from '@angular/common/http'; describe('SnackbarsnackbarService ', () => { let snackbarService: SnackbarService; let appRefSpy: jasmine.SpyObj<ApplicationRef>; beforeEach(() => { appRefSpy = jasmine.createSpyObj('ApplicationRef', ['attachView', 'detachView', 'injector']); TestBed.configureTestingModule({ declarations: [SnackbarComponent], imports: [HttpClientTestingModule, CommonModule], providers: [SnackbarService, { provide: ApplicationRef, useValue: appRefSpy } ], }).compileComponents(); snackbarService = TestBed.inject(SnackbarService); appRefSpy = TestBed.inject(ApplicationRef) as jasmine.SpyObj<ApplicationRef>; }); it('should be created', () => { expect(snackbarService).toBeTruthy(); }); it('should show a snackbar and attach it to the view', () => { const message = 'Test message'; snackbarService.showSnackbar('success', message); expect(appRefSpy.attachView).toHaveBeenCalled(); expect(document.body.querySelector('.snackbar')).toBeTruthy(); }); it('should apply the correct configuration to the snackbar', () => { const config: SnackbarConfig = { type: 'error', message: 'An error occurred', options: { vertical: 'bottom', horizontal: 'center' }, }; snackbarService.showSnackbar(config.type, config.message, config.options); const snackbarElement = document.body.querySelector('.snackbar'); expect(snackbarElement).toBeTruthy(); expect(snackbarElement?.classList.contains('error')).toBeTrue(); expect(snackbarElement?.classList.contains('bottom')).toBeTrue(); expect(snackbarElement?.classList.contains('center-horizontal')).toBeTrue(); }); it('should close the snackbar and remove it from the view', () => { snackbarService.showSnackbar('info', 'Test info message'); snackbarService.closeSnackbar(); expect(appRefSpy.detachView).toHaveBeenCalled(); expect(document.body.querySelector('.snackbar')).toBeNull(); }); it('should clear timeout when the snackbar is closed manually', () => { spyOn(window, 'clearTimeout'); snackbarService.showSnackbar('info', 'Test info message'); snackbarService.closeSnackbar(); expect(clearTimeout).toHaveBeenCalled(); }); it('should close the snackbar automatically after the specified duration', fakeAsync(() => { const duration = 10000; snackbarService.showSnackbar('info', 'Test auto-close message', {}, duration); tick(duration) expect(document.body.querySelector('.snackbar')).toBeNull(); })); it('should handle HttpErrorResponse messages', () => { const errorResponse = new HttpErrorResponse({ status: 500, statusText: 'Server Error' }); spyOn(snackbarService, 'closeSnackbar').and.callThrough(); snackbarService.showSnackbar('error', errorResponse); expect(appRefSpy.attachView).toHaveBeenCalled(); expect(snackbarService.closeSnackbar).not.toHaveBeenCalled(); }); }); The only test that passes is the 'should be created' And this error persists: Angular was not able to inject a renderer (RendererFactory2). Likely this is due to a broken DI hierarchy. Make sure that any injector used to create this component has a correct parent Continue reading...