簡體   English   中英

如何測試ngrx路由器存儲選擇器

[英]How to test ngrx router store selector

在我們的應用程序中,我們有一個簡單的存儲,在根級別包含一個AuthState和一個RouterState RouterState是通過@ngrx/router-store方法創建的。

我們有一些選擇器必須使用RouterState來檢索例如一個參數,然后將它與例如其他選擇器結果組合。

我們的問題是我們無法設法找到正確設置測試套件的方法,以便能夠測試這樣的組合選擇器。

減速機設置

App模塊導入

StoreModule.forRoot(reducers, { metaReducers }),
StoreRouterConnectingModule.forRoot({
  stateKey: 'router',
}),
StoreDevtoolsModule.instrument(),

reducers如下:

減速

export interface RouterStateUrl {
  url: string;
  queryParams: Params;
  params: Params;
}

export interface State {
  router: fromNgrxRouter.RouterReducerState<RouterStateUrl>;
  auth: fromAuth.AuthState;
}

export const reducers: ActionReducerMap<State> = {
  router: fromNgrxRouter.routerReducer,
  auth: fromAuth.reducer,
};

export const getRouterState = createFeatureSelector<fromNgrxRouter.RouterReducerState<RouterStateUrl>>('router');

export const getRouterStateUrl = createSelector(
  getRouterState,
  (routerState: fromNgrxRouter.RouterReducerState<RouterStateUrl>) => routerState.state
);

export const isSomeIdParamValid = createSelector(
  getRouterState,
  (routerS) => {
    return routerS.state.params && routerS.state.params.someId;
  }
);

這是AuthState reducer:

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<AuthState>('auth');
export const getIsLoggedIn = createSelector(
  getAuthState,
  (authState: AuthState) => {
    return authState.loggedIn;
  }
);

export const getMixedSelection = createSelector(
  isSomeIdParamValid,
  getIsLoggedIn,
  (paramValid, isLoggedIn) => paramValid && isLoggedIn
)

測試設置

@Component({
  template: ``
})
class ListMockComponent {}

describe('Router Selectors', () => {
  let store: Store<State>;
  let router: Router;

  beforeEach(() => {
    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);
  });

測試及其結果

測試1

it('should retrieve routerState', () => {
  router.navigateByUrl('/list/123');
  store.select(getRouterState).subscribe(routerState => console.log(routerState));
});

{router:{state:{url:'/ list / 123',params:{someId:123},queryParams:{}},navigationId:1},auth:{loggedIn:false}}

正如你所看到的getRouterState選擇不檢索只有router的狀態,但片包含整個對象routerState + authState State router和auth是此對象的子級。 因此選擇器無法檢索正確的切片。

測試2

it('should retrieve routerStateUrl', () => {
  router.navigateByUrl('/list/123');
  store.select(getRouterStateUrl).subscribe(value => console.log(value));
});

undefined - TypeError:無法讀取未定義的屬性“state”

測試3

it('should retrieve mixed selector results', () => {
  router.navigateByUrl('/list/123');
  store.select(getMixedSelection).subscribe(value => console.log(value));
});

未定義

TypeError:無法讀取未定義的屬性“state”

TypeError:無法讀取{auth:{}的屬性'loggedIn',路由器:{}}

注意

請注意語法

StoreModule.forRoot({
  // How to add auth at that level
  router: combineReducers(reducers)
}),

如果我們想要使用多個reducer組合選擇器,似乎是強制性的。 我們可以使用forRoot(reducers)但是我們不能只測試路由器選擇器。 國家的其他部分將不存在。

例如,如果我們需要測試:

export const getMixedSelection = createSelector(
  isSomeIdParamValid,
  getIsLoggedIn,
  (paramValid, isLoggedIn) => paramValid && isLoggedIn
)

我們需要路由器和auth。 我們找不到合適的測試設置,允許我們使用AuthStateRouterState測試這樣的組合選擇器。

問題

如何設置此測試以便我們基本上可以測試我們的選擇器?

當我們運行應用程序時,它完美運行。 所以問題只在於測試設置。

我們認為使用真實路由器設置testBed可能是錯誤的想法。 但我們很難模擬routerSelector(僅)並給它一個模擬的路由器狀態片僅用於測試目的。

僅模擬這些路由器選擇器真的很難。 store.select上進行間諜活動很容易但是在store.select(routerSelectorMethod)上進行間諜活動,方法作為參數變得一團糟。

我自己也在努力解決這個問題,路由器狀態的'state'屬性是未定義的。 我找到了適合我的解決方案是調用router.initialNavigation()來啟動RouterTestingModule,后者又設置了路由器存儲。

在我的情況下,我需要測試一個使用根存儲選擇器和功能存儲選擇器的CanActivate防護。 下面的測試模塊設置適用於我:

describe('My guard', () => {

   let myGuard: MyGuard;
   let router: Router;
   let store: Store<State>;

   beforeEach(async(() => {
       TestBed.configureTestingModule({
           imports: [
               RouterTestingModule.withRoutes([
                   {
                       path: '',
                       redirectTo: 'one',
                       pathMatch: 'full'
                   },
                   {
                       path: 'one',
                       component: MockTestComponent
                   },
                   {
                       path: 'two',
                       component: MockTestComponent
                   }
               ]),
               StoreModule.forRoot({
                   ...fromRoot.reducers,
                   'myFeature': combineReducers(fromFeature.reducers)
               }),
               StoreRouterConnectingModule.forRoot({
                   stateKey: 'router', // name of reducer key
               }),
           ],
           declarations: [MockTestComponent],
           providers: [MyGuard, {provide: RouterStateSerializer, useClass: CustomSerializer}]
       }).compileComponents();

       myGuard = TestBed.get(MyGuard);
       router = TestBed.get(Router);
       store = TestBed.get(Store);
       spyOn(store, 'dispatch').and.callThrough();
       router.initialNavigation();
   }));
});

現在,您可以使用projector屬性模擬選擇器依賴項:

我-reducer.ts

export interface State {
  evenNums: number[];
  oddNums: number[];
}

export const selectSumEvenNums = createSelector(
  (state: State) => state.evenNums,
  (evenNums) => evenNums.reduce((prev, curr) => prev + curr)
);
export const selectSumOddNums = createSelector(
  (state: State) => state.oddNums,
  (oddNums) => oddNums.reduce((prev, curr) => prev + curr)
);
export const selectTotal = createSelector(
  selectSumEvenNums,
  selectSumOddNums,
  (evenSum, oddSum) => evenSum + oddSum
);

我-reducer.spec.ts

import * as fromMyReducers from './my-reducers';

describe('My Selectors', () => {

  it('should calc selectTotal', () => {
    expect(fromMyReducers.selectTotal.projector(2, 3)).toBe(5);
  });

});

取自官方文檔

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM