40

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);
    });
  }));
});
1
  • 1
    @Carsten the test doesnt work. Seems to be something wrong with the syntax for the subscribe part of the test Commented Aug 23, 2017 at 12:40

4 Answers 4

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';
Sign up to request clarification or add additional context in comments.

6 Comments

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 :)
This worked! Do you think its a worthwhile test? Or should I only test my service rather than a component?
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.
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?
what is the return type of getUsers method in User interface / class?
|
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();
    });
  }));

1 Comment

:) thanks, for me instead of "subscribe", "then" worked fine, litle change :- fixture.whenStable().then((response) => { expect(homeComponent.listOfUsers).toEqual(response); done(); });
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);
}));

Comments

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);
    });
  }));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.