簡體   English   中英

如何在Angular 4單元測試中模擬@ngrx / store狀態

[英]How to mock @ngrx/store state in Angular 4 unit tests

我正在使用@ngrx / store和@ngrx / effect開發一個Angular 4應用程序,我正在嘗試為我的效果編寫一些單元測試。

我的一個效果應該是使用服務從后端獲取一些數據。 從后端檢索值后,應每隔一段時間觸發此效果,以生成輪詢效果並更新本地數據。

由於輪詢間隔是在我的應用程序狀態配置的,我希望能夠模擬狀態存儲,以便我可以在測試期間更改它。

這是我的SPEC:

describe('Order Effects', () => {

  let actions: Observable<any>;
  let effects: OrderEffect;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        StoreModule.forRoot({ orders: ordersReducer })
      ],
      providers: [
        OrderEffect,
        provideMockActions(() => actions),
        { provide: OrderService, useValue: jasmine.createSpyObj('orderService', ['getAllOrders']) }
      ]
    });

    effects = TestBed.get(OrderEffect);
  });


  it('should load orders and call load orders complete action', () => {

    const mockData = [{ id: 1 }, { id: 2 }, { id: 3 }];

    const orderService = TestBed.get(OrderService);
    orderService.getAllOrders.and.returnValue(Observable.of(mockData));

    actions = hot('--a-', { a: new orderActions.LoadAllOrdersAction(new Date()) });

    const expectedEffect = cold('--b|', { b: new orderActions.LoadAllOrdersCompleteAction(mockData) });
    expect(effects.loadAllOrders$).toBeObservable(expectedEffect);
  });

});

這是效果:

@Injectable()
export class OrderEffect {

  @Effect()
  loadAllOrders$: Observable<Action> = this.actions.ofType(orderActions.LOAD_ALL_ORDERS)
    .withLatestFrom(this.store$) // Get the latest state from the store and add as the second value of the array below
    .switchMap((input: any[]) => {
      const action: orderActions.LoadAllOrdersAction = input[0];
      const store: AppState = input[1];

      return Observable.timer(0, store.orders.pollingInterval) // Timer to create the polling effect.
        .switchMap(() => this.orderService.getAllOrders(action.payload)
          .map((orders: Array<any>) => new orderActions.LoadAllOrdersCompleteAction(orders))
          .catch(error => Observable.throw(new orderActions.LoadAllOrdersFailAction(error)))
        );
    });

  constructor(private actions: Actions, private orderService: OrderService, private store$: Store<AppState>) { }
}

在效果代碼中,我使用: .withLatestFrom(this.store$)獲取當前狀態,我想模擬這個屬性: store.orders.pollingInterval 誰能幫我?

如果您需要獲得效果中的商店狀態,則在測試此效果時必須監視商店,以便您可以模擬其狀態。

describe('Order Effects', () => {
    // ...
    let ordersStateMock = {
        orders: {
            pollingInterval: null
        }
    };

    beforeEach(() => {
        const storeSpy = jasmine.createSpyObj('storeSpy', [
            'dispatch', 'subscribe'
        ]);
        storeSpy.select = () => Observable.of(ordersStateMock);
        // ...
    });
});

然后在您所在的州,您可以為商店狀態設置所需的值:

it('should load orders and call load orders complete action', () => {
    // ...
    ordersStateMock.orders.pollingInterval = 2;
    actions = hot('--a-', { a: new orderActions.LoadAllOrdersAction(new Date()) });
    // ...
});

我在測試中使用此解決方案。 我在“使用Angular和ngrx進行反應性編程”一書中得到了它。 它指的是ngrx和Angular v2,但它仍然非常有用,因為它解釋了反應式編程和關鍵特性的概念。

希望這可以幫助!

我一直在查看@ngrx文檔,我找到了另一個模擬商店狀態的可能答案(參見下面的完整代碼):

let actions;
let effects: OrderEffect;
let ordersStateMock;

beforeEach(() => {

  ordersStateMock = {
    orders: {
      pollingInterval: 30
    }
  };

  TestBed.configureTestingModule({
    imports: [
      StoreModule.forRoot(
        { orders: ordersReducer },
        { initialState: ordersStateMock }
      )
    ],
    providers: [
      OrderEffect,
      provideMockActions(() => actions),
      { provide: OrderService,
        useValue: jasmine.createSpyObj('orderService', ['getAllOrders'])
      }
    ]
  });

  effects = TestBed.get(OrderEffect);
});

顯然我只需要在StoreModule導入中設置初始狀態。

建議的解決方案對我不起作用。 我使用的是ngrx 5.2.0(帶有ngrx實體)和Angular 5.2.9。 metaReducer的想法如下所述: https//github.com/ngrx/platform/issues/414#issuecomment-331354701

在我的情況下,我想模仿一些狀態。 所以我的metaReducer看起來像:

  export function mockMetaReducer(reducer: ActionReducer<any>): ActionReducer<any, any> {
    return function(state: any, action: any): any {
      return reducer(ordersStateMock, action);
    };
  }

這個解決方案是最簡單的,適用於ngrx:5.2.0

為基於真實商店的商店創建模擬

export class StoreMock<T = any> extends Store<T> {

    public source = new BehaviorSubject<any>({});

    public overrideState(allStates: any) {
        this.source.next(allStates);
    }

}

並在測試中使用它

let store: StoreMock<YourStateModel>;

TestBed.configureTestingModule({
    providers: [
        { provide: Store, useClass: StoreMock },
    ]
});
store = TestBed.get(Store);


beforeEach(() => {
    store.overrideState({
        currentStateName: MOCKED_STATE_DATA_OBJECT
    });
});

暫無
暫無

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

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