mercredi 3 octobre 2018

Angular - How to properly test actions that are performed in Observable subscription block?

I have to write unit tests for an angular 5 application. In order to do that I use jasmine + jest (we use jest instead of karma in my company for test speed).

In order to test my component behaviour (see code below), I create a test that subscribe to the same Observable as the component under test, then, wait 2 seconds in the hope that the component's subscription code block had enougth time to complete, and then, look for component's internal changes.

The problem is that as the number of test increase, the time needed for the tests to complete increase too. And I personally think that there must be a better way of testing this type of code below.

  • How would you handle this situation ? I tried looking at async but couldn't find a way to make it fit my needs.
  • How can I ensure that my tests are running after the component's subscription block ?
  • How can I avoid waiting 2 seconds for the test to start and instead just wait for the component's subscription block to complete ?

Thanks in advance for your help.


  • Component:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SomeService } from './some.service';

@Component({
  selector: 'app-dummy',
  templateUrl: './dummy.component.html',
  styleUrls: ['./dummy.component.scss']
})
export class DummyComponent implements OnInit, OnDestroy {
  isEditable: Boolean;
  //...
  private aSubscriber;

  constructor(private someService: SomeService) {
    this.aSubscriber = someService.anObservable$.subscribe(value => {
      this.isEditable = value;
    });
  }

  ngOnInit() { }

  ngOnDestroy() {
    this.aSubscriber.unsubscribe();
  }
}
  • Service:
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class SomeService {
  private aSubject = new Subject<any>();
  
  anObservable$ = this.aSubject.asObservable();
  
  constructor() { }

  notify(value) {
    this.aSubject.next(value);
  }
}
  • Spec file:
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';

import { DummyComponent } from './dummy.component';
import { SomeService } from './some.service';

describe('DummyComponent', () => {
  let component: DummyComponent;
  let fixture: ComponentFixture<DummyComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [DummyComponent],
      providers: [SomeService]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(DummyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should subscribe to anObservable and set values according to the received one',
    inject([SomeService], (service: SomeService) => {
      service.anObservable$.subscribe(value => {
        setTimeout(() => { }, 2000);
        //Test that values are correctly set in the component under test.
        expect(component.isEditable).toBeTruthy();
        //...
      });

      service.notify(true);
  }));
});

Aucun commentaire:

Enregistrer un commentaire