I have fetchBooks
method that retrieve data from backend and then populates books
variable.
import { Component, OnInit } from "@angular/core";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
})
export class AppComponent implements OnInit {
isLoading: boolean;
books;
constructor(private booksService: BooksService) {}
ngOnInit() {
this.fetchBooks();
}
fetchBooks(): void {
this.isLoading = true;
this.booksService
.fetchBooks()
.subscribe(
response => {
this.isLoading = false;
this.books = response.data;
// ..
},
error => {
this.isLoading = false;
// ..
}
);
}
}
I need to write unit tests for isLoading
flag. I wrote something like this
it('should work', async () => {
sut.fetchBooks();
// ...
expect(sut.isLoading).toBe(true);
// ...
expect(sut.isLoading).toBe(false);
});
But I struggle with the rest. Maybe someone knows how to solve it or knows some article that explains it?
My solution is to make use of a Subject to control the emission of values. Pass subject.asObservable()
to the spy which your SUT is subscribing to.
import { Subject } from 'rxjs';
it('should correctly work', () => {
// replace SomeType with the type returned by booksService.fetchData()
const subject = new Subject<SomeType>();
spyOn(booksService, 'fetchData').and.returnValue(subject.asObservable());
const mockData: SomeType = {}; // put some data here, same type;
// bonus, you may want to check also that isLoading is initially false
// expect(sut.isLoading).toBe(false);
// check: after triggered, isLoading should be true until the subscription returns a value
sut.ngOnOnit();
expect(sut.isLoading).toBe(true);
// check: after the subscription receives a value, isLoading should be false
subject.next(mockData);
expect(sut.isLoading).toBe(false);
// bonus
expect(sut.data).toEqual(mockData);
expect(booksService.fetchData).toHaveBeenCalledTimes(1);
});