Megan
Megan

Reputation: 247

How to mock @ngrx/store?

I want to unit test a component. I am using @ngrx/store and rxjs in order to update my store from my component. To do that, I subscribe and unsubscribe to a store. My unit test is failing with

TypeError: undefined is not an object (evaluating 'this.store.unsubscribe') in config/spec-bundle.js

I am not able to find how to correctly mock my store and its event emitters.

Here are the following lines:

app.component.ts :

  ngOnDestroy() {
    // unsubscribe the observable
    this.userStore.unsubscribe();
  }

app.component.spec.ts :

how I mock the store :

        export class MockStore extends Subject<fromRoot.State> implements Store <fromRoot.State> {}

how I configure :

        TestBed.configureTestingModule({
        imports: [
            FormsModule,
            ReactiveFormsModule,
            StoreModule.provideStore(reducer)
        ],
        declarations: [LoginComponent],
        providers: [
            BaseRequestOptions,
            MockBackend,
            { provide: AuthService, useValue: authServiceStub },
            { provide: LoginService, useValue: loginServiceStub },
            { provide: LoggerService, useValue: loggerServiceStub },
            {
                provide: Http, useFactory: (backend: ConnectionBackend,
                    defaultOptions: BaseRequestOptions) => {
                    return new Http(backend, defaultOptions);
                }, deps: [MockBackend, BaseRequestOptions]
            },
            { provide: Router, useClass: MockRouter }
        ]
  })

Tell me if you want more informations. Thanks in advance.

Upvotes: 4

Views: 8317

Answers (2)

cartant
cartant

Reputation: 58440

There's more than one way that you can mock the store and what you do is going to depend upon how you want to test.

When I mock the store in my tests, I'm only interested in two things:

  • being able to easily emit a state that's relevant to the test; and
  • being able to see any actions that are dispatched to the store during the test.the

I'm not interested in wiring up reducers, as they are tested elsewhere, so this is what I do.

I use a function like this to create a Store from two Subject instances:

import { Action, Store } from "@ngrx/store";
import { Subject } from "rxjs/Subject";

export function mockStore<T>({
  actions = new Subject<Action>(),
  states = new Subject<T>()
}: {
  actions?: Subject<Action>,
  states?: Subject<T>
}): Store<T> {

  let result = states as any;
  result.dispatch = (action: Action) => actions.next(action);
  return result;
}

And in a beforeEach I create the Store and add it to the TestBed's providers:

actions = new Subject<Action>();
states = new Subject<AppState>();
store = mockStore<AppState>({ actions, states });

TestBed.configureTestingModule({
  imports: [EffectsTestingModule],
  providers: [
    {
      provide: Store,
      useValue: store
    },
    ...
  ]
});

With the mocked Store injected in the components - or effects, or whatever you are testing - it's then simple to emit a particular state within a test - using the states subject - and it's easy to check to see that any expected actions have been dispatched - by subscribing to the actions subject.

Regarding your error, you should not be calling unsubscribe on the Store; you should call it on the Subscription object that's returned when you subscribe to the store - as mentioned in the other answer.

Upvotes: 9

ArcQ
ArcQ

Reputation: 31

The issue might be with your unsubscribe. You should be calling unsubscribe on your actually subscription instead of your observable/store. eg. if you have subscription like this:

this.subscription = this._store
    .select('users')
    .subscribe(users => {
      this.users = users;
  });

you unsubscribe like this:

  ngOnDestroy() {
    // unsubscribe the observable
    this.subscription.unsubscribe();
  }

Also, not sure if you left out some code, but the way you mocked your store, it'd probably be sufficient to just use the actual store, and just spy on dispatch etc.

Upvotes: 1

Related Questions