Angular - unit test for a subscribe function in a component
Asked Answered
Y

4

39

Angular 4 unit test for a subscribe.

I want to test that my subscribe returns an array of Users. I want to mock a list of users and test a function called getUsers.

The subscribe unit test doesnt work. Something wrong with the syntax.

This is my Users interface:

export interface User {
  id: number;
  name: string;
  username: string;
  email: string;
  address: {
    street: string;
    suite: string;
    city: string;
    zipcode: string;
    geo: {
      lat: string;
      lng: string;
    }
  };
  phone: string;
  website: string;
  company: {
    name: string;
    catchPhrase: string;
    bs: string;
  };
};

This is my component I want to test:

import { Component, OnInit } from "@angular/core";
import { Observable } from "rxjs/Observable";

import { UserService } from "../../services/user.service";
import { User } from "../../models/user.model";

@Component({
  selector: "home-users",
  templateUrl: "./home.component.html"
})

export class HomeComponent implements OnInit {
  private listOfUsers: User[];

  constructor(private userService: UserService) {
  }

  ngOnInit() {
    this.getUsers();
  }

  getUsers(): void {
    this.userService.getUsers().subscribe(users => {
      this.listOfUsers = users;
    });
  }
}

This is my unit test attempt:

import { TestBed, async, inject } from "@angular/core/testing";
import { HttpModule } from "@angular/http";

import { HomeComponent } from "./home.component";
import { UserService } from "../../services/user.service";
import { User } from "../../models/user.model";

describe("HomeComponent", () => {
  let userService;
  let homeComponent;
  let fixture;
  let element;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        HomeComponent
      ],
      providers: [
        UserService
      ],
      imports: [HttpModule]
    }).compileComponents();
  }));

  beforeEach(inject([UserService], s => {
    userService = s;
    fixture = TestBed.createComponent(HomeComponent);
    homeComponent = fixture.componentInstance;
    element = fixture.nativeElement;
  }));

  it("should call getUsers and return list of users", async(() => {
    // Arrange
    let response: User[] = [];

    // Act
    homeComponent.getUsers();

    fixture.detectChanges();
    fixture.whenStable().subscribe(() => {
        expect(homeComponent.listOfUsers).toEqual(response);
    });
  }));
});
Ysabel answered 23/8, 2017 at 12:38 Comment(1)
@Carsten the test doesnt work. Seems to be something wrong with the syntax for the subscribe part of the testYsabel
K
70

You need this for version rxjs@6 and above. For older rxjs version answer is below:

import { of } from 'rxjs';

it("should call getUsers and return list of users", async(() => {
  const response: User[] = [];

  spyOn(userService, 'getUsers').and.returnValue(of(response))

  homeComponent.getUsers();

  fixture.detectChanges();
    
  expect(homeComponent.listOfUsers).toEqual(response);
}));

For old rxjs version change import from:

import { of } from 'rxjs';

to

import { of } from 'rxjs/observable/of';
Kendricks answered 23/8, 2017 at 12:46 Comment(6)
Actually I can not try now :D Did this work? I added jest support and yesterday everything was working. But now I get some strange errors :)Kendricks
This worked! Do you think its a worthwhile test? Or should I only test my service rather than a component?Ysabel
I think you need test if you have some logic. For example you have some data received from server and you have server processing this data. This is critical logic and must not be broken. You have to unit test this. In your example: well why not if it is easy to test. This will defend future developers from accidently removing this subscription.Kendricks
Anyone has an error at of(response) in the spyOn line? mine is User[] not assignable to void.. how to resolve it since there is no return value supposedly?Nonperformance
what is the return type of getUsers method in User interface / class?Kendricks
Are you sure you spy on the service? Component has the same getUsers method with void return type. Looks like you spied on the component method.Kendricks
N
6

I had similar issue and to make it work I used the arbitrary function (in the following code it's named done) inside of it


  it("should call getUsers and return list of users", async((done) => {
    // Arrange
    let response: User[] = [];

    // Act
    homeComponent.getUsers();

    fixture.detectChanges();
    fixture.whenStable().subscribe(() => {
        expect(homeComponent.listOfUsers).toEqual(response);
        done();
    });
  }));
Noun answered 16/12, 2020 at 12:39 Comment(1)
:) thanks, for me instead of "subscribe", "then" worked fine, litle change :- fixture.whenStable().then((response) => { expect(homeComponent.listOfUsers).toEqual(response); done(); });Gona
M
4

in your case you can use fakeAsync also used tick() to detect change. you can add time to tick also to indicate how log to wait. eg tick(1000)

Code is modified from Sharikov Vladislav

import { fakeAsync, getTestBed, TestBed, tick } from '@angular/core/testing';

it("should call getUsers and return list of users", fakeAsync(() => {
  const response: User[] = [];
  spyOn(userService, 'getUsers').and.returnValue(of(response))
  homeComponent.getUsers();
  tick();
  expect(homeComponent.listOfUsers).toEqual(response);
}));
Muco answered 16/2, 2021 at 4:35 Comment(0)
S
0

You just need to use fakeAsync if you are using jest framework with Angular. You can use mockReturnValue to mock return value.

E.g

  it('should return reports when getReports called', fakeAsync(() => {
    const mockData = [
      {
        id: '1',
        reportName: 'Annual Report 2023',
      }
    ];
    jest.spyOn(service, 'getReports').mockReturnValue(of(mockData))
    service.getReports().subscribe( res => {
      expect(res).toEqual(mockData);
    });
  }));
Strongarm answered 19/2 at 10:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.