我正在尝试为Angular中的服务编写单元测试。 我想模拟ngrx的store.select函数,因此我可以测试服务对存储选择器返回的不同值的反应。我希望能够分别模拟每个选择器。
我的主要问题是如何模拟参数化选择器。
我以前使用过一个BehaviourSubject,它映射到select函数,但这不允许您为不同的选择器返回不同的值。它不可读,因为您在嘲笑哪个选择器并不明显。
选择1:使用主题模拟存储:无法知道主题对应的选择器,无法为不同的选择器返回不同的值。
// service.spec.ts
const selectSubject = new BehaviourSubject(null);
class MockStore {
select = () => selectSubject;
}
选择2:使用开关模拟存储:适用于不同的选择器,但是当选择器具有参数时无法使其工作。
// service.spec.ts
// This works well but how can I make it work with selectors with parameters??
const firstSubject = new BehaviourSubject(null);
const secondSubject = new BehaviourSubject(null);
class MockStore {
select = (selector) => {
switch (selector): {
case FirstSelector: {
return firstSubject;
}
case SecondSelector: {
return secondSubject;
}
}
};
}
describe('TestService', () => {
let service: TestService;
let store: Store<any>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
TestService,
{ provide: Store, useClass: MockStore }
],
});
service = TestBed.get(TestService);
store = TestBed.get(Store);
});
it('should do X when first selector returns A and second selector returns B', () => {
firstSelectorSubject.next(A);
secondSelectorSubject.next(B);
// Write expectation
});
});
我要模拟的带有参数化选择器的服务方法,因此可以测试具有不同id值的getUserName
getUserName(id: string): Observable<string> {
return this.store.select(getUser(id)).pipe(
filter(user => user !== null),
map(user => user.fullName)
);
}
答案 0 :(得分:2)
我已经解决了类似的问题有一段时间了,我想我已经找到了让它发挥作用的方法。
与选择器
export const getItemsByProperty = (property: string, value: any) => createSelector(getAllItems, (items: ItemObj[]) => items.filter((item) => item[property] == value));
在哪里
export const getAllItems = createSelector(getState, (state) => selectAll(state.items));
在我的组件单元测试文件中,我用数据覆盖了 getItemsByProperty 的底层选择器调用 getAllItems 的选择器,然后在我的测试中期待过滤后的数据。如果要返回的内容发生变化,只需更新 getAllItems 的结果即可。
答案 1 :(得分:1)
NgRx 7.0包含@ ngrx / store / testing用于模拟Store。有一个overrideSelector方法非常方便。您基本上是在模拟选择器的输出,因此参数无关紧要。
https://medium.com/ngconf/mockstore-in-ngrx-v7-0-f7803708de4e
mockStore.overrideSelector(yourSelector, expectedOutput);
您还可以在MockStore的初始化中设置选择器:
const mockStore = new MockStore<fromState.IState>(
new MockState(),
new ActionsSubject(),
null,
null,
[{
selector: yourSelector,
value: expectedOutput
}]
);
如果要实际测试选择器,则应具有专门用于选择器的测试文件。要测试参数化的选择器,您将使用投影仪方法。它接受选择器作用的状态片(或对象)以及任何参数。在我的示例中,我正在测试NgRx实体。
selector.ts:
export const getEntityById: any = createSelector(
getEntitiesAsDictionary,
(entities, props) => entities[props.id]
);
spec.ts:
const expectedId = 111;
const expected = { id: expectedId , user: { userId: expectedId } };
const iEntities = {
[expectedId ]: expected,
[222]: { id: 222, user: { userId: 222 }},
[333]: { id: 333, user: { userId:333 }}
};
const actual = fromState.getEntityById.projector(iEntities, { id: expectedId });
expect(actual).toEqual(expected);