I am currently learning about unit testing and I am facing an issue with calling a function inside an *ngFor loop in an Angular 12 application. I have an observable that contains an array of objects and it is working correctly, iterating through the data properly.
Below is the HTML code:
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Category</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of games$ | async; let i = index">
<td>{{ i+1 }}</td>
<td>{{ item.name }}</td>
<td class="badge-cell">
{{ item.categoryId }}
</td>
<td class="center action-cell">
<button type="button"
class="delete-game btn"
(click)="deleteGame(item.id)">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- /.table-responsive-->
The problem occurs when trying to test a function call inside the loop. Accessing the nativeElement property of a fixture shows that the dynamic part of the table is null, which means the deleteGame function is not available.
Here is what console.log(fixture.nativeElement) looks like:
<div _ngcontent-a-c74="" class="table-responsive">
<table _ngcontent-a-c74="" id="dataTables-example" class="table table-striped">
<thead _ngcontent-a-c74="">
<tr _ngcontent-a-c74="">
<th _ngcontent-a-c74="">#</th>
<th _ngcontent-a-c74="">Name</th>
<th _ngcontent-a-c74="">Category</th>
<th _ngcontent-a-c74="">Action</th>
</tr>
</thead>
<tbody>
//----------NO ROWS IN THE LOG------------//
<!--bindings={
"ng-reflect-ng-for-of": null
}-->
</tbody>
</table>
</div>
Static data is being used to simulate the original array (see GAME_DATA). Here are the tests:
describe('ListGamesComponent', () => {
let component: ListGamesComponent;
let fixture: ComponentFixture<ListGamesComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, CommonModule],
declarations: [ListGamesComponent],
providers: [
RequestService,
],
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(ListGamesComponent);
component = fixture.componentInstance as any;
component.games$ = from([GAME_DATA]) as unknown as Observable<
IGameData[]
>;
});
})
);
it('should create', () => {
expect(component).toBeTruthy();
});
it('should check if deleteGame was called', fakeAsync(() => {
spyOn(component, 'deleteGame');
component.games$.subscribe((data: any) => {
// ---- logged an observable, a proper list of games is returned ----
console.log('data: ', data);
});
fixture.detectChanges();
let button = fixture.debugElement.query(By.css('.delete-game'));
button.triggerEventHandler('click', null);
fixture.detectChanges();
tick();
expect(component.deleteGame).toHaveBeenCalled();
}));
});
In addition, I logged an observable inside the test and it retrieved the data from GAME_DATA static file, so I am confused as to why the table is not being generated.
UPDATE
This is how games$ is implemented in the component:
export class ListGamesComponent implements OnInit {
ngOnInit(): void {
let pager: { index: number; size: number } = {
index: this.pageIndex,
size: this.pageSize,
};
this.games$ = this.gameService.getAllGames(pager).pipe(
tap((data) => {
this.totalRecords = data.totalItems;
this.gamesList = data.games;
this.setPage(1, false); // generate pagination
}),
pluck('games')
);
}
}
Any assistance would be highly appreciated. Thank you!