loading...
Cover image for Mocking your ngrx store in a unit test

Mocking your ngrx store in a unit test

nickraphael profile image Nick Raphael ・Updated on ・2 min read

I hope you're all avidly unit testing all your code. Yes, cough, sure, of course we are. Look, we've all been there. We commonly put off writing unit tests because of a deadline and when we get around to writing them, our code looks untestable. Usually this is because we see our code is dependant on other services and we can't see how to tease it apart so we can write a targeted unit test.

In this post I'm going to unit test a component that takes the ngrx store as a constructor dependancy.

Here is my component...

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';

import { CustodianModel } from '../../../../../model/custodian.model';
import { fadeIn } from '../../../../../theme/animations/fadein-animation';
import {
  DeleteCustodian,
  LoadCustodians
} from '../../state/custodians/actions';
import { ICustodiansState } from '../../state/custodians/reducer';
import {
  getCustodiansAll,
  getCustodiansLoading
} from '../../state/custodians/selectors';

@Component({
  selector: 'custodians',
  templateUrl: './custodians.component.html',
  styleUrls: ['./custodians.component.css'],
  animations: [fadeIn]
})
export class CustodiansComponent implements OnInit {
  custodians$: Observable<CustodianModel[]>;
  custodiansLoading$: Observable<boolean>;

  constructor(private store: Store<ICustodiansState>) {}

  ngOnInit() {
    this.custodians$ = this.store.select(getCustodiansAll);
    this.custodiansLoading$ = this.store.select(getCustodiansLoading);

    this.store.dispatch(new LoadCustodians());
  }

  deleteCustodian(custodianId: number) {
    this.store.dispatch(new DeleteCustodian({ custodianId }));
  }
}

You can see the component takes a Store as a dependancy. We don't want to rely on our ngrx store in our unit test. However, some tests will require data from the store. We certainly don't want our unit tests to have to load data via our ngrx effects from a backend web api. Luckily for us, ngrx has it covered with the provideMockStore method.

provideMockStore enables us to populate our store within our unit test.

import { NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';

import { PipeModule } from '../../../../../pipe.module';
import { TranslateService } from '../../../../../services/translate/translate.service';
import { CustodiansComponent } from './custodians.component';

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

  beforeEach(async(() => {
    const initialState = {
      'admin-console': {
        custodians: {
          ids: [],
          entities: {},
          loading: false
        }
      }
    };

    TestBed.configureTestingModule({
      declarations: [CustodiansComponent],
      providers: [
        provideMockStore({ initialState })
      ],
      schemas: [NO_ERRORS_SCHEMA]
    });
  }));

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

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

I've only included the simplest of unit tests here. But you can see how easy it is to call provideMockStore and pass in a state object.

Discussion

pic
Editor guide