我正在使用@ 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
。任何人都可以帮助我吗?
答案 0 :(得分:8)
如果您需要在效果中获取商店状态,则在测试此效果时必须监视商店,以便您可以模拟其状态。
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,但它仍然非常有用,因为它解释了反应式编程和关键特性的概念。
希望这有帮助!
答案 1 :(得分:5)
我一直在查看@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
导入中设置初始状态。
答案 2 :(得分:0)
建议的解决方案对我不起作用。我使用的是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);
};
}
答案 3 :(得分:0)
This solution是最简单的方法,它与 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
});
});