Unit testing with Angular 12 - Unable to function call inside ngFor loop
Before we fix it, I will try to explain to you what is going wrong so you get a better understanding.
The first call of fixture.detectChanges()
is when ngOnInit
is called (very important to remember this).
// we change the definition of games$
component.games$ = from([GAME_DATA]) as unknown as Observable<
IGameData[]
>;
// we subscribe and see the data
component.games$.subscribe((data: any) => {
// ---- logged an observable, a proper list of games is returned ----
console.log('data: ', data);
});
// we call fixture.detectChanges(); and this calls ngOnInit
fixture.detectChanges();
The problem is that ngOnInit
re-assigns this.games$
to a service call therefore losing the definition previously assigned. That's why you see the issue. I hope that makes sense.
To fix it:
I like using createSpyObj
for faking the dependencies of external services, follow the lines with !! in the comments.
describe('ListGamesComponent', () => {
let component: ListGamesComponent;
let fixture: ComponentFixture<ListGamesComponent>;
// !! create a mock game service
let mockGameService: jasmine.SpyObj<GameService>;
beforeEach(
waitForAsync(() => {
// !! assign mockGameService to a spy object with public method of
// getAllGames
mockGameService = jasmine.createSpyObj<GameService>('GameService', ['getAllGames']);
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, CommonModule],
declarations: [ListGamesComponent],
providers: [
RequestService,
// !! provide fake GameService for the real GameService
{ provide: GameService, useValue: mockGameService },
],
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(ListGamesComponent);
component = fixture.componentInstance as any;
});
})
);
it('should create', () => {
expect(component).toBeTruthy();
});
it('should check if deleteGame was called', fakeAsync(() => {
spyOn(component, 'deleteGame');
// !! make the mock return a value before ngOnInit is called
mockGameService.getAllGames.and.returnValue(from([GAME_DATA]) as unknown as Observable<
IGameData[]
>);
fixture.detectChanges();
let button = fixture.debugElement.query(By.css('.delete-game'));
button.triggerEventHandler('click', null);
fixture.detectChanges();
tick();
expect(component.deleteGame).toHaveBeenCalled();
}));
});
Check this link out on how to mock external dependencies.