2

When using the provide mockStore, my feature/selector functions only see state as undefined.

Due to this, I am only able to mock the selectors and cannot therefore perform a more fully integrated test.

Are selectors not supposed to see state when it's provided by the mock store or should this work?

Creating the mock store

     TestBed.configureTestingModule({
        providers: [
            AuthGuardGuard,
            provideMockStore({
                initialState: { ...initialState },
            }),
        ],
    });

Then logging the state that the selector should see, which is undefined.

export const currentUser = createSelector(
    authRootFeature,
    (state: fromAuthRoot.State) => {
        console.log(state); // Undefined
        return state.auth.currentUser;
    }
);
JoshuaTree
  • 1,211
  • 14
  • 19

1 Answers1

6

MockStore should be setting the state, I'll share with you two implementations that work and you can choose which one you prefer, there's missing info in your question so it's hard to point out why this is not working for you.

Here's one:

My selector:

export const selectFeature = createFeatureSelector<State, FeatureState>('feature');

export const getFeatureProperty = createSelector(
  selectFeature,
  (state) => state.featureProperty
);

My test file:

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let mockStore: MockStore<State>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [LogsMenuViewComponent, LogsMenuViewDropdownComponent],
      providers: [provideMockStore({ initialState: { feature: { featureProperty: 123 } } as State })],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(MyComponent);
    mockStore = TestBed.inject(Store) as MockStore<State>;
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
});

The reason this works is because my selector uses a feature, selector, so it's able to get the state properly.

Another implementation that might work for you is to override the selector, like this:

  describe('selectors', () => {
    let mockShowsSelector;
    beforeEach(() => {
      mockShowsSelector = store.overrideSelector(selectFavoriteShows, [{
        id: 3,
        name: 'C',
        description: 'Show C',
        imgUrl: '',
        isFavorite: true
      }]);
      fixture.detectChanges();
    });

    it('should render all favorite shows', () => {
      expect(fixture.debugElement.queryAll(By.css('.mat-card')).length).toBe(1);
    });

    it('should update the UI when the selector changes', () => {
      mockShowsSelector.setResult([ // overide selector
        {
          id: 1,
          name: 'A',
          description: 'Show A',
          imgUrl: '',
          isFavorite: true
        },
        {
          id: 2,
          name: 'B',
          description: 'Show B',
          imgUrl: '',
          isFavorite: true
        }
      ]);
      store.refreshState();
      fixture.detectChanges();
      expect(fixture.debugElement.queryAll(By.css('.mat-card')).length).toBe(2);
    });
  })
Yair Cohen
  • 2,032
  • 7
  • 13
  • 1
    Thank you for the example. It seems like my feature selector was be a bit incorrect. Made some changes and was able to get the mockStore selections working! – JoshuaTree Jan 01 '21 at 14:08
  • 1
    I was just working on this today so was happy to see your question :) If you're looking for resources on testing NgRx, I can recommend this video: https://www.youtube.com/watch?v=NOT-nJLDnyg&t=994s&ab_channel=ThisDotMedia – Yair Cohen Jan 01 '21 at 14:10
  • 1
    Thanks again, really appreciate it and that video was excellent. I was not using compileComponents() as I was testing a service. It seems like not using compileComponents results in an incomplete store instance. – JoshuaTree Jan 02 '21 at 10:49
  • 1
    This line `mockStore = TestBed.inject(Store) as MockStore;` is what I needed! Thank you! – kim Mar 16 '22 at 08:34