I struggle a bit with testing adding new message with primeng's MessageService.
Here is my component's code:
@Component({
selector: 'app-get-menu',
templateUrl: './get-menu.component.html',
styleUrls: ['./get-menu.component.scss'],
providers: [MessageService],
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [Toast],
})
export class GetMenuComponent implements OnInit {
// Var
menu: MenuInterface[] = [];
// Constructor
constructor(
private apiService: ApiService,
private router: Router,
private dateTransformerService: DateTransformerService,
private messageService: MessageService
) {}
ngOnInit() {
this.initData();
}
initData() {
this.apiService.getMenu().subscribe({
next: (menu) => {
this.menu = menu;
},
error: (error) => {
this.messageService.add({
severity: "error",
summary: "Error",
detail: "Couldn't load menu",
life: 10000,
});
}
});
}
Here is my spec.ts
code:
describe('GetMenuComponent', () => {
let component: GetMenuComponent;
let fixture: ComponentFixture<GetMenuComponent>;
let apiService: ApiService;
let messageService: MessageService;
let router: Router;
let dateTransformerService: DateTransformerService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [GetMenuComponent],
providers: [ApiService,MessageService, provideHttpClient(),
provideHttpClientTesting(), Router, DateTransformerService]
});
fixture = TestBed.createComponent(GetMenuComponent);
fixture.detectChanges();
component = fixture.componentInstance;
apiService = TestBed.inject(ApiService);
router = TestBed.inject(Router);
dateTransformerService = TestBed.inject(DateTransformerService);
messageService = TestBed.inject(MessageService);
});
//already tried without the fakeAsync wrapper
it("Should handle errors and show error message", fakeAsync(() => {
spyOn(apiService, 'getMenu').and.returnValue(throwError(() => new Error("some error")));
//already tried with spyOn(apiService, 'getMenu').and.throwError("some error");
spyOn(messageService, 'add');
component.initData();
tick(200);
expect(messageService.add).toHaveBeenCalled();
}))
The test always fail with the following error:
Expected spy add to have been called.
If I replace the messageService.add({...})
call to another random function call in my component and try to test if this random function is being called, the test succeed. Am I missing something?
The first thing I notice is that configureTestingModule
is not wrapped in a waitForAsync
. The method is asynchronous and should be wrapped by waitForAsync
before running the test cases.
Basics of testing components - CLI-generated tests - Angular.dev
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [GetMenuComponent],
providers: [ApiService,MessageService, provideHttpClient(),
provideHttpClientTesting(), Router, DateTransformerService]
});
fixture = TestBed.createComponent(GetMenuComponent);
fixture.detectChanges();
component = fixture.componentInstance;
apiService = TestBed.inject(ApiService);
router = TestBed.inject(Router);
dateTransformerService = TestBed.inject(DateTransformerService);
}));
Could be a timing issue. Just swap our tick
with flush
so that the API is completed and does not depend on timing (200
).
You are having MessageService
in the providers array of the component, so it will be a separate instance from the root service. You have to access this service through the component as shown below.
it("Should handle errors and show error message", fakeAsync(() => {
spyOn(apiService, 'getMenu').and.returnValue(throwError(() => new Error("some error")));
//already tried with spyOn(apiService, 'getMenu').and.throwError("some error");
spyOn(component['messageService'], 'add');
component.initData();
flush();
expect(component.messageService.add).toHaveBeenCalled();
}))