在单元测试中模拟带有参数的ngrx存储选择器(角度)

时间:2019-04-17 23:59:56

标签: angular unit-testing rxjs store ngrx

我正在尝试为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)
    );
  }

2 个答案:

答案 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);