@ngrx/effects: 测试一个效果 returns empty()

@ngrx/effects: testing an effect returns empty()

我正在使用@ngrx/effects 4.1.1。我的效果是 returns 一个像这样的空可观察对象:

@Effect() showDialog$: Observable<Action> = this
.actions$
.ofType( ActionTypes.DIALOG_SHOW )
.map( ( action: DialogAction ) => action.payload )
.switchMap( payload => {
    this.dialogsService.showDialog( payload.className );
    return empty();
} );

我正尝试在 these guidelines 之后编写一个单元测试,以测试该效果是否会产生一个空的可观察对象。我有这个:

describe( 'DialogEffects', () => {
    let effects: DialogEffects;
    let actions: Observable<any>;
    const mockDialogService = {
        showDialog: sinon.stub()
    };

    beforeEach( () => {
        TestBed.configureTestingModule( {
            providers: [
                DialogEffects, provideMockActions( () => actions ),
                {
                    provide: DialogsService,
                    useValue: mockDialogService
                }
            ]
        } );

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

    describe( 'showDialog$', () => {
        it( 'should return an empty observable', () => {
            const dialogName = 'testDialog';
            const action = showDialog( dialogName );

            actions = hot( '--a-', { a: action } );
            const expected = cold( '|' );

            expect( effects.showDialog$ ).toBeObservable( expected );
        } );
    } );
} );

然而,Karma (v1.7.1) 抱怨:

Expected [ ] to equal [ Object({ frame: 0, notification: Notification({ kind: 'C', value: undefined, error: undefined, hasValue: false }) }) ].

如何测试 returns empty() 的效果?我尝试使用 dispatch: false 修改效果元数据,但这没有效果。

想法?

问题是您将实际结果与 cold('|') 进行比较。

cold('|')中的竖线表示可观察流的完成。但是,您的效果不会完成。 empty() observable 确实完成了,但是从 switchMap 返回 empty() 只是看到 observable 合并到 effect observable 的流中——它没有完成 effect observable。

相反,您应该将实际结果与 cold('') - 一个不发出任何值且未完成的可观察对象进行比较。

最好的方法是在 effect 中使用 dispatch false 并更改 swicthMap 以进行点击

@Effect({dispatch: false}) showDialog$ = this.actions$.pipe(
   ofType( ActionTypes.DIALOG_SHOW ),
   map( ( action: DialogAction ) => action.payload ),
   tap( payload => 
      this.dialogsService.showDialog( payload.className )
    ));

要测试它,您可以像

describe( 'showDialog$', () => {
    it( 'should return an empty observable', () => {
        const dialogName = 'testDialog';
        const action = showDialog( dialogName );
        actions = hot( '--a-', { a: action } );

       expect(effects.showDialog$).toBeObservable(actions);
// here use mock framework to check the dialogService.showDialog is called
    } );
} );

请注意我使用 toBeObservable(actions) 的原因,那是因为由于 disptach false 这个效果不会产生任何东西,所以 actions 和以前一样,你通过调用 toBeObservable(actions) 获得的是使测试同步,因此您可以在它之后检查您的服务是否使用 jasmine 或 ts-mockito

等模拟框架调用

要测试效果不派发,您可以使用 getEffectsMetadata 函数断言它的元数据:

expect(getEffectsMetadata(effects).deleteSnapshotSuccess$?.dispatch).toBe(false)

以上已经用ngrx 9.0.0测试过。由于所有 observables 都会发出,而不管 dispatch,像 expect(effects.effect$).toBeObservable(cold('')) 这样的检查不会产生预期的结果。