Mocking NgRx store with Spectator

2.9k views Asked by At

I'm trying to write unit tests together with Angular, Jest and Spectator. Right now I'm struggling with mocking NgRx store. I'm getting a "No provider for MockStore" error when trying to get an instance of the NgRx provided MockStore from the Spectator instance.

I've read the NgRx documentation regarding mocking and unit tests and I'm able to mock NgRx store without using Spectator by reading the documentation, I've tried Googling around for help but can't seem to find examples of mocking NgRx store together with Spectator and I've left a message on NgRx/store Gitter to see if anyone there is able to help me.

Here's what my code looks like:

    let component: MyComponent;
    let spectator: Spectator<MyComponent>;
    let mockStore: MockStore<selectors.AppState>;
    let mockUsernameSelector: MemoizedSelector<AppState, string>;

    const createComponent = createComponentFactory({
        component: MyComponent,
        componentProviders: [
            provideMockStore(),
        ],
        shallow: true,
        detectChanges: false,
    });

    beforeEach(() => {
        spectator = createComponent();
        component = spectator.component;
        mockStore = spectator.inject<MockStore<selectors.AppState>>(MockStore);
        mockUsernameSelector = mockStore.overrideSelector(selectors.selectUserName, 'Bob');
    });

But as I said, as soon as I try to get instance of the MockStore from Spectator via spectator.inject (also tried spectator.get), I get the "No provider for MockStore" -error. I've also tried to provide the MockStore like this:

    const createComponent = createComponentFactory({
        component: MyComponent,
        componentProviders: [
            mockProvider(MockStore, provideMockStore()),
        ],
        shallow: true,
        detectChanges: false,
    });

But then I get the error "No provider for Store", obviously.

Perhaps someone here is able to help me? Thanks in advance!

3

There are 3 answers

1
Kamil Naja On

You can use convienient mockProvider function.

In providers array:

import { createComponentFactory, mockProvider, Spectator, SpyObject } from '@ngneat/spectator/jest';
import { Store } from '@ngrx/store';

providers: [mockProvider(Store)],
0
mat.hudak On

I had the same issue, this seems to be working for me:

In setup-jest.ts I've defined global injections with common test imports and provider for store using provideMockStore with initial state:

import { provideMockStore } from '@ngrx/store/testing';

let initialState = MockStoreInitialState; // constant built from all reducers initial states

defineGlobalsInjections({
  imports: [
    MaterialModule,
    NoopAnimationsModule,
    ReactiveFormsModule,
    FormsModule,
    SharedModule,
    TranslateTestingModule.withTranslations({ en: require('./assets/i18n/en.json') }),
  ],
  providers: [provideMockStore({ initialState })],
});

Then when testing it in components I'm using MockStore and spies:

describe('SampleCardComponent', () => {
  let mockStore;
  let dispatchSpy;
  let spectator: Spectator<SampleCardComponent>;

  const createComponent = createTestComponentFactory({
    component: SampleCardComponent,
    mocks: [ExperimentTimePipe],
  });

  beforeEach(() => {
    spectator = createComponent({
      props: { sampleHolder: SAMPLE_MOCK },
    });
    mockStore = spectator.get(MockStore);
    dispatchSpy = jest.spyOn(mockStore, 'dispatch');
  });

  test('should create', () => {
    expect(spectator).toBeTruthy();
  });

  test('should call editSample function', () => {
    const handleEditSpy = jest.spyOn(spectator.component, 'handleEdit');

    spectator.click('.button-edit');
    spectator.detectChanges();
    expect(handleEditSpy).toHaveBeenCalled();
    expect(dispatchSpy).toHaveBeenCalledTimes(1);
    expect(dispatchSpy).toHaveBeenCalledWith({//...actionObject})
  });
});

Honestly, I've no idea if this is the best way but it works. If you see some way to improve this I'll be glad to hear it.

0
Aram On

You can get it just by using spectator.inject(MockStore).

And don't forget to add your mock initial state to provideMockStore({ initialState })

import { Spectator, createComponentFactory } from '@ngneat/spectator';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { mockInitialState } from 'src/mocks/state/app-state.mock';
import { AppComponent } from './app.component';
import { AppState } from './state/app.state';

describe('AppComponent', () => {
  let store: MockStore<AppState>;
  const initialState = mockInitialState;
  let spectator: Spectator<AppComponent>;

  const createComponent = createComponentFactory({
    component: AppComponent,
    declarations: [
      AppComponent,
      ...
    ],
    imports: [...],
    providers: [ provideMockStore({ initialState })],
  });

  beforeEach(() => {
    spectator = createComponent();
    store = spectator.inject(MockStore);
  });

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

  it('check state', () => {
    const isLoggedIn = {
      isLoggedIn: {
        login: true
      },
    };

    const state = {
      ...initialState,
      ...isLoggedIn,
    };

    store.setState(state);
    spectator.fixture.detectChanges();
    expect(spectator.component.isLoggedIn).toBe(true);
  });
});