title: How to test ngrx router store selector
body: <p>In our app we have a simple store, containing at root level an <code>AuthState</code> and a <code>RouterState</code>. The <code>RouterState</code> is created through <code>@ngrx/router-store</code> methods.</p>
  
  <p>We have some selectors which have to use the RouterState to retrieve for example a param and then combine it for example with other selector result.</p>
  
  <p>Our problem is we cannot manage to find a way to correctly setup test suite to be able to test such combined selectors.</p>
  
  <h1>Reducer setup</h1>
  
  <h2>App module imports</h2>
  
  <pre><code>StoreModule.forRoot(reducers, { metaReducers }),
  StoreRouterConnectingModule.forRoot({
    stateKey: 'router',
  }),
  StoreDevtoolsModule.instrument(),
  </code></pre>
  
  <p><code>reducers</code> beeing the following:</p>
  
  <h2>Reducers</h2>
  
  <pre><code>export interface RouterStateUrl {
    url: string;
    queryParams: Params;
    params: Params;
  }
  
  export interface State {
    router: fromNgrxRouter.RouterReducerState&lt;RouterStateUrl&gt;;
    auth: fromAuth.AuthState;
  }
  
  export const reducers: ActionReducerMap&lt;State&gt; = {
    router: fromNgrxRouter.routerReducer,
    auth: fromAuth.reducer,
  };
  
  export const getRouterState = createFeatureSelector&lt;fromNgrxRouter.RouterReducerState&lt;RouterStateUrl&gt;&gt;('router');
  
  export const getRouterStateUrl = createSelector(
    getRouterState,
    (routerState: fromNgrxRouter.RouterReducerState&lt;RouterStateUrl&gt;) =&gt; routerState.state
  );
  
  export const isSomeIdParamValid = createSelector(
    getRouterState,
    (routerS) =&gt; {
      return routerS.state.params &amp;&amp; routerS.state.params.someId;
    }
  );
  </code></pre>
  
  <p>Here is AuthState reducer:</p>
  
  <pre><code>export interface AuthState {
    loggedIn: boolean;
  }
  
  export const initialState: AuthState = {
    loggedIn: false,
  };
  
  export function reducer(
    state = initialState,
    action: Action
  ): AuthState {
    switch (action.type) {
      default: {
        return state;
      }
    }
  }
  
  export const getAuthState = createFeatureSelector&lt;AuthState&gt;('auth');
  export const getIsLoggedIn = createSelector(
    getAuthState,
    (authState: AuthState) =&gt; {
      return authState.loggedIn;
    }
  );
  
  export const getMixedSelection = createSelector(
    isSomeIdParamValid,
    getIsLoggedIn,
    (paramValid, isLoggedIn) =&gt; paramValid &amp;&amp; isLoggedIn
  )
  </code></pre>
  
  <h1>Test setup</h1>
  
  <pre><code>@Component({
    template: ``
  })
  class ListMockComponent {}
  
  describe('Router Selectors', () =&gt; {
    let store: Store&lt;State&gt;;
    let router: Router;
  
    beforeEach(() =&gt; {
      TestBed.configureTestingModule({
        imports: [
          RouterTestingModule.withRoutes([{
            path: 'list/:someId',
            component: ListMockComponent
          }]),
          StoreModule.forRoot({
            // How to add auth at that level
            router: combineReducers(reducers)
          }),
          StoreRouterConnectingModule.forRoot({
            stateKey: 'router',
          }),
        ],
        declarations: [ListMockComponent],
      });
  
      store = TestBed.get(Store);
      router = TestBed.get(Router);
    });
  </code></pre>
  
  <h2>tests and their result</h2>
  
  <h3>Test 1</h3>
  
  <pre><code>it('should retrieve routerState', () =&gt; {
    router.navigateByUrl('/list/123');
    store.select(getRouterState).subscribe(routerState =&gt; console.log(routerState));
  });
  </code></pre>
  
  <blockquote>
    <p>{ router:
           { state: { url: '/list/123', params: {someId: 123}, queryParams: {} },
             navigationId: 1 },
          auth:
           { loggedIn: false } }</p>
  </blockquote>
  
  <p>as you can see the <code>getRouterState</code> selector doesn't retrieve only the <code>router</code> slice of the state but an object containing the whole <code>routerState</code> + <code>authState</code> <code>State</code>. router and auth are children of this object. So the selector doesn't manage to retrieve proper slices.</p>
  
  <h3>Test 2</h3>
  
  <pre><code>it('should retrieve routerStateUrl', () =&gt; {
    router.navigateByUrl('/list/123');
    store.select(getRouterStateUrl).subscribe(value =&gt; console.log(value));
  });
  </code></pre>
  
  <blockquote>
    <p>undefined - TypeError: Cannot read property 'state' of undefined</p>
  </blockquote>
  
  <h3>Test 3</h3>
  
  <pre><code>it('should retrieve mixed selector results', () =&gt; {
    router.navigateByUrl('/list/123');
    store.select(getMixedSelection).subscribe(value =&gt; console.log(value));
  });
  </code></pre>
  
  <blockquote>
    <p>undefined </p>
    
    <p>TypeError: Cannot read property 'state' of undefined</p>
    
    <p>TypeError: Cannot read property 'loggedIn' of {auth: {}, router: {}}</p>
  </blockquote>
  
  <h2>Note</h2>
  
  <p>Please note the syntax </p>
  
  <pre><code>StoreModule.forRoot({
    // How to add auth at that level
    router: combineReducers(reducers)
  }),
  </code></pre>
  
  <p>seems mandatory if we want to combine selectors using multiple reducers. We could just use <code>forRoot(reducers)</code> but then we cannot test ONLY router selectors. Other parts of state would be inexistent.</p>
  
  <p>For example, if we need to test:</p>
  
  <pre><code>export const getMixedSelection = createSelector(
    isSomeIdParamValid,
    getIsLoggedIn,
    (paramValid, isLoggedIn) =&gt; paramValid &amp;&amp; isLoggedIn
  )
  </code></pre>
  
  <p>we need both router and auth. And we cannot find a proper test setup which allows us to test such a combined selector using <code>AuthState</code> and <code>RouterState</code>.</p>
  
  <h1>The problem</h1>
  
  <p>How to setup this test so we can basically test our selectors?</p>
  
  <p>When we run the app, it works perfectly. So the problem is only with testing setup.</p>
  
  <p>We thought maybe it was a wrong idea to setup a testBed using real router. But we struggle to mock the routerSelector (only) and give it a mocked router state slice for testing purpose only.</p>
  
  <p>It's really hard to mock these router selectors only. Spying on <code>store.select</code> is easy but spying on the <code>store.select(routerSelectorMethod)</code>, with method as argument becomes a mess.</p>