Suppose you have an Angular component that is retrieving data.

You set loading to true before calling subscribe and set it back to false in a finalize pipe:

import { Component, OnInit } from '@angular/core';
import { finalize } from 'rxjs/operators';

@Component({
  selector: 'app-my-component',
  template: `<div *ngIf="loading">Loading...</div>`
})
export class MyComponent implements OnInit {
  loading = false;

  constructor(private myService: MyService) {}

  ngOnInit(): void {
    this.loadData();
  }

  private loadData(): void {
    this.loading = true;
    this.myService
      .get()
      .pipe(finalize(() => (this.loading = false)))
      .subscribe();
  }
}

Code language: TypeScript (typescript)

This is very simplified, but you get the idea. The component loads some data while showing a loading state. get method of the service returns an Observable. Component template binds to the loading variable.

So how do you write a unit test for the loading state?

We need to ensure loading is set to true during the call and back to false when it completes.

    it('should set loading to true while loading data', fakeAsync(() => {
      const mockResult = new Subject<any>();
      jest.spyOn(myService, 'get').mockReturnValue(mockResult);

      fixture.detectChanges(); // Observable subscription fired here via ngOnInit
      tick();

      expect(getLoader().loading).toBe(true);
      mockResult.complete(); // Observable finalize triggered here
      fixture.detectChanges(); // Needed to update loader state in HTML
      expect(getLoader().loading).toBe(false);
    }));

Code language: JavaScript (javascript)

The test above checks the loading state changes while data is being loaded.

Step by step:

  1. Setup a mock Subject: First, we create a fake result to control the subscription in the component
  2. Spy on the service: Then, we’re keeping an eye on the service to see if get() is called. If it is, we’ll give back the fake result we created earlier
  3. Check for loading state: Check if loading spinner is showing. It should be showing because we haven’t loaded the data yet at this point
  4. Complete the Subject: Calling complete() on mock subject triggers finalize pipe in the component.
  5. Updating the page: Then, we update the page to reflect the loading variable state using fixture.detectChanges()
  6. Checking again: Finally, we check again if the loading part of the page is still showing. This time, we expect it to be hidden because we’ve finished loading the data.

To summarise, we created a unit test to ensure the loading message appears only data is being loaded. Tests like this can be hard to get your head around, but they’re worth writing. 

Happy testing!

Umut Esen

Software Engineer specialising in full-stack web application development.

Leave a Reply