使用 Jest 的不可理解的单元测试效果错误

Posted

技术标签:

【中文标题】使用 Jest 的不可理解的单元测试效果错误【英文标题】:Ununderstandable unit testing effects error using Jest 【发布时间】:2019-11-01 23:20:20 【问题描述】:

我正在处理两个几乎相同的测试用例,我应该断言期望两个 NgRx 效果返回一个布尔值流。 在第一个测试用例中一切都按预期工作,尽管对第二个测试用例做同样的事情,但我无法让它按预期工作。无论我做什么,接收到的值总是一个空数组:

expect(received).toEqual(expected) // deep equality
    - Expected
    + Received

    - Array [
    -   Object 
    -     "frame": 10,
    -     "notification": Notification 
    -       "error": undefined,
    -       "hasValue": true,
    -       "kind": "N",
    -       "value": true,
    -     ,
    -   ,
    - ]
    + Array []

工作测试

logger.effects.spec.ts:

describe('LoggerEffects', () => 
    let actions$: Observable<ApiFailure>;
    let effects: LoggerEffects;
    const serviceMock = <AppLoggerServiceI> failure: ( body, title ) => of(true) ;
    beforeEach(() => 
        TestBed.configureTestingModule(
            imports: [StoreModule.forRoot()],
            providers: [
                LoggerEffects,
                provideMockActions(() => actions$),
                
                    provide: 'appLoggerService',
                    useValue: serviceMock
                
            ]
        );
        effects = TestBed.get(LoggerEffects);
    );
    const failure = new HttpErrorResponse( error: 'HttpErrorResponse' );
    describe('logApiFailure$', () => 
        it('should return a stream of boolean if the dispatched action is ApiActions.searchFailure', () => 
            const action = ApiActions.searchFailure( failure );
            actions$ = hot('-a--',  a: action );
            const expected = cold('-b--',  b: true );
            expect(effects.logApiFailure$).toBeObservable(expected);
        );
    );
);

logger.effects.ts:

logApiFailure$ = createEffect(
        () =>
            this.actions$.pipe(
                ofType(ApiActions.searchFailure),
                exhaustMap(( failure:  body, title  ) =>
                    this.appLoggerService.failure(
                        body,
                        title
                    )
                )
            ),
         dispatch: false 
    );

测试失败

router.effects.spec.ts:

describe('RouterEffects', () => 
    let actions$: Observable<boolean>;
    let effects: RouterEffects;

    const routerMock = <Partial<Router>> navigate: (commands: any[]) => Promise.resolve(true) ;

    let router: Router;
    beforeEach(() => 
        TestBed.configureTestingModule(
            imports: [StoreModule.forRoot()],
            providers: [
                RouterEffects,
                provideMockActions(() => actions$),
                
                    provide: Router,
                    useValue: routerMock
                
            ]
        );
        effects = TestBed.get(RouterEffects);
    );
    describe('navigateTo$', () => 
        it('should return a stream of boolean if the dispatched action is RouterActions.navigateTo', () => 
            const action = RouterActions.navigateTo();
            actions$ = hot('-a--',  a: action );
            const expected = cold('-b--',  b: true );
            console.log('toto', effects.navigateTo$);
            expect(effects.navigateTo$).toBeObservable(expected);
        );
    );
);

router.effects.ts:

navigateTo$ = createEffect(
    () =>
        this.actions$.pipe(
            ofType(RouterActions.navigateTo),
            exhaustMap(() => this.router.navigate(['my-route', ]))
        ),
     dispatch: false 
);

此时,我假设 router.effects.spec.ts 中的 Hot Observable 有问题,这会阻止效果调度 navigateTo 操作。你对此有什么想法吗?

项目的依赖关系


    "dependencies": 
        "@angular/animations": "^8.0.0",
        "@angular/cdk": "^8.0.0",
        "@angular/common": "^8.0.0",
        "@angular/compiler": "^8.0.0",
        "@angular/core": "^8.0.0",
        "@angular/forms": "^8.0.0",
        "@angular/material": "^8.0.0",
        "@angular/material-moment-adapter": "^8.0.0",
        "@angular/platform-browser": "^8.0.0",
        "@angular/platform-browser-dynamic": "^8.0.0",
        "@angular/router": "^8.0.0",
        "@ngrx/effects": "^8.0.0",
        "@ngrx/router-store": "^8.0.0",
        "@ngrx/store": "^8.0.0",
        "core-js": "^2.5.4",
        "hammerjs": "^2.0.8",
        "moment": "^2.24.0",
        "rxjs": "^6.5.0",
        "tslib": "^1.9.0",
        "zone.js": "^0.9.1"
    ,
    "devDependencies": 
        "jest-preset-angular": "7.0.0",
        "@angular-devkit/build-angular": "^0.800.0",
        "@angular-devkit/build-ng-packagr": "~0.800.0",
        "@angular-devkit/core": "^8.0.2",
        "@angular-devkit/schematics": "^8.0.2",
        "@angular/cli": "8.0.0",
        "@angular/compiler-cli": "^8.0.0",
        "@angular/language-service": "^8.0.0",
        "@ngrx/schematics": "^8.0.0",
        "@ngrx/store-devtools": "^8.0.0",
        "@nrwl/cypress": "8.1.0",
        "@nrwl/jest": "8.1.0",
        "@nrwl/workspace": "8.1.0",
        "@nrwl/schematics": "8.1.0",
        "@types/jest": "24.0.9",
        "@types/node": "^12.0.7",
        "codelyzer": "~5.0.1",
        "cypress": "~3.3.1",
        "dotenv": "6.2.0",
        "jest": "24.1.0",
        "ng-packagr": "^5.1.0",
        "prettier": "1.16.4",
        "ts-jest": "24.0.0",
        "ts-node": "^8.2.0",
        "tsickle": "^0.35.0",
        "tslib": "^1.9.0",
        "tslint": "~5.11.0",
        "typescript": "~3.4.5"
    

【问题讨论】:

【参考方案1】:

我刚刚花了一些时间跟踪一个类似的问题(空预期数组),结果证明该问题与未由 TestScheduler 管理的异步代码有关。就我而言,我使用的是from([Promise])。为了解决这个问题,我模拟了返回一个 promise 的函数以返回一个可观察的 of()

这个问题对我帮助很大,并清楚地解释了问题: https://github.com/cartant/rxjs-marbles/issues/11

在你的情况下,我认为问题是由 this.router.navigate(['my-route', ]) 引起的,它返回一个承诺,而不是一个可观察的。

尝试模拟 router.navigate 以返回 of(true)

【讨论】:

以上是关于使用 Jest 的不可理解的单元测试效果错误的主要内容,如果未能解决你的问题,请参考以下文章

Jest单元测试

在 Nuxt、Vue、jest 和 Vuetify 中为项目编写单元测试

Vue/Typescript/Jest - Jest 单元测试语法错误:意外的令牌导入

基于 Jest + Enzyme 的 React 单元测试

渲染中的错误:“TypeError:无法读取未定义的属性'_t'”在使用 Jest 的单元测试中

在 NestJS 中使用 Jest 和 MongoDB 进行单元测试