Writing unit test for subscription inside onInit() in Angular

21,520

First solution:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SpinnerComponent } from './spinner.component';
import { of } from 'rxjs/observable/of';

describe('SpinnerComponent', () => {
  let component: SpinnerComponent;
  let fixture: ComponentFixture<SpinnerComponent>;
  const fakeSpinner = {
    spinnerState: of({ show: false }),
  };

  beforeEach(
    async(() => {
      TestBed.configureTestingModule({
        declarations: [SpinnerComponent],
        providers: [{ provide: SpinnerService, useValue: fakeSpinner }],
      }).compileComponents();
    }),
  );

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

  it('should set component.visible based on spinnerService state', () => {
    expect(component.visible).toEqual(false)
  });
});

The important thing to note is that we are providing our mock implementation of the spinner service.

Second solution:

The other way to test this would be to create a shallow test, without using Angular testing utilities at all. Since you do not care about the template interactions, you can also test this just like you would test regular ts classes.

class FakeSpinnerService {
  private spinnerStateSource = new Subject();
  spinnerState = this.spinnerStateSource.asObservable();

  emit(val: boolean) {
    this.spinnerStateSource.next({ show: val });
  }
}

it('should set component.visible based on spinnerService state', () => {
  const fakeService = new FakeSpinnerService();
  const component = new SpinnerComponent(fakeService as any);

  // initial value
  expect(component.visible).toBe(true);

  component.ngOnInit();

  fakeService.emit(false);
  expect(component.visible).toBe(false);

  fakeService.emit(true);
  expect(component.visible).toBe(true);
});
Share:
21,520
Chetan Khilare
Author by

Chetan Khilare

Updated on July 09, 2022

Comments

  • Chetan Khilare
    Chetan Khilare almost 2 years

    I have a Spinner Component class as below which would show/hide Progress Spinner using Angular material.

    export class SpinnerComponent implements OnInit, OnDestroy {
        visible = true;
        private subscription: Subscription;
    
        constructor(private spinnerService: SpinnerService) {}
    
        ngOnInit() {
             this.subscription = this.spinnerService.spinnerState.subscribe((state: SpinnerState) => {
                    this.visible = state.show;
               });
         }
        ngOnDestroy() {
             if (this.subscription) {
                this.subscription.unsubscribe();
             }
        }
    }
    

    How can I write a spec which could test for the specific line shown below which is inside the ngOnInit() method ?

    this.visible = state.show;
    
  • Talk is Cheap Show me Code
    Talk is Cheap Show me Code over 3 years
    I have one more question, if you can answer please, so I have set-timeout inside a subscriber which also inside ngOninit setTimeout(() => { document.querySelector(entityIdentifier).classList.remove('h‌​ighlight'); }, 2000); so what can be done in this situation>
  • kuldeep
    kuldeep over 3 years
    thanks for this answer, however i am unable to understand in second scenario why fixture.detectChanges does not work ? I was scratching my head for hours before stumbling upon your answer. i subscribe to my subject and then put the expect inside it with done(); at the end. This works with component.ngOnInit but does not works with fixture.detectChanges, any idea why ?