4

I have a smart component test for my NgRx implementation that looks something like this:

describe( 'Component', () => {
  let store: MockStore<State>;

  beforeEach( async( () => {
    TestBed.configureTestingModule( {
        /* ... */
        providers: [
            provideMockStore( { initialState: fromReducer.initialState } )
        ]
    } ).compileComponents();
    store = TestBed.get<Store<State>>( Store );
  } ) );

  it( 'should load items in #ngOnInit', () => {
    store.setState( {
        item: {
          ...fromReducer.initialState,
          entities: { [item.id]: item },
        },
        otherFeature: null,
        otherFeature: null,
        otherFeature: null
    } );
    component.items$.subscribe( items =>
        store.select( ItemStoreSelectors.selectItems ).subscribe( fromStore => expect( items ).toEqual( fromStore ) )
    );
  } );
});

I use provideMockStore and setState to mock my NgRx state. Everything works fine this way, but I really don't like this part of it:

store.setState( {
    item: {
      ...fromReducer.initialState,
      entities: { [item.id]: item },
    },
    otherFeature: null,
    otherFeature: null,
    otherFeature: null
} );

I have to add every other feature slice of my state to the setState function. Otherwise Typescript will throw Errors.


So preferably I don't want to set the root state, but rather a specific feature slice like this:

store.setState( {
  ...fromReducer.initialState,
  entities: { [item.id]: item },
} );

I couldn't find any documentation on how to use provideMockStore for specific slices of the state here.

Florian Ludewig
  • 4,338
  • 11
  • 71
  • 137

2 Answers2

8

I would recommend taking a different approach here. What you should do instead is unit test your selector and component independently.

NgRx 8 allows for mock selectors. Use that in your component to verify the component's logic is correct.

Then, unit test your selector independently to verify that your selector is working as intended.

This way the tests are independent, not brittle, and truly unit tests.

Edit: The docs I linked are official, but an alternative way to mock your selectors is:

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        provideMockStore({
          selectors: [
            {
              selector: yourSelectorNameHere,
              value: someMockValueHere
            }
          ]
        })
      ]
    });

xandermonkey
  • 4,054
  • 2
  • 31
  • 53
  • I already did independent unit tests for reducer, effects and selectors. This is actually for a smart component. So I don't event need to test the smart component? – Florian Ludewig Jul 31 '19 at 16:20
  • No, you should certainly test the component. However, don't test it by actually allowing the selector to run. Mock it, and test the logic in the component based on the expected behavior you mock. – xandermonkey Jul 31 '19 at 16:32
  • alright, same for effects?... because I am using the mock store in my effects tests, too – Florian Ludewig Jul 31 '19 at 16:37
  • 1
    Yes, same for effects. If you allow your selectors to actually run, and there's a bug in your selector, the test for your component / effect will fail. It won't fail due to a bug in the code it's trying to test -- it would fail due to the bug in your selector! That's why you should mock it. – xandermonkey Jul 31 '19 at 16:41
  • Thank you!... I'll take a look at my code tomorrow to check if I have any other concerns and then check you answer :) – Florian Ludewig Jul 31 '19 at 16:44
0

As xander mentioned you can use mockSelectors. The reason why TS is complaining is because how you typed it.

MockStore<State> is probably your whole state tree, for just a feature slice, use MockStore<FeatureState>

timdeschryver
  • 14,415
  • 1
  • 19
  • 32