开玩笑不处理 RxJS observable 的 subscribe() 中的 expect() 错误

Posted

技术标签:

【中文标题】开玩笑不处理 RxJS observable 的 subscribe() 中的 expect() 错误【英文标题】:Jest not handling errors from expect() in subscribe() of RxJS observable 【发布时间】:2019-05-18 11:33:21 【问题描述】:

我一直在尝试让 Jest 与 RxJS 一起工作,但在 Jest 无法从订阅回调内部传播错误时遇到问题。

这是一个我尝试过但不起作用的示例测试:

import of from 'rxjs';

test('should fail', () => 
  of(false).subscribe(val => 
    expect(val).toBe(true);
  );
);

上述测试应该失败,但它通过了。我搜索了一下,找到了以下解决方案:

Failing expect() inside subscribe() does not mark test as invalid

这建议在玩笑中使用“完成”语法来解决问题。虽然使用“done”回调确实会导致上述测试失败,但这种方法存在许多问题:

无法描述的错误

测试失败,因为 subcribe() 中的 'expect' 调用引发错误,导致 'done()' 永远不会被调用。然后测试超时,等待完成。因此,它不会传播 'expect' 错误,而是导致超时错误,这意味着在 expect 子句中失败的每个测试都将显示超时错误,而不是失败的 'expect' 调用的实际错误消息。

测试需要更长的时间才能失败

因为所有测试都因超时错误而失败,这意味着每个测试需要 5 秒才能失败(异步测试在 5 秒后超时)。这可以显着增加测试运行的时间

done 使用不当

done 回调旨在支持异步用例进行测试。但是 rxjs 不一定是异步的。我上面内联的代码实际上是同步运行的。例如,以下测试将通过:

import of from 'rxjs';

test('should pass', () => 
  let didRunSynchronously = false;
  of(true).subscribe(() => 
    didRunSynchronously = true;
  );
  expect(didRunSynchronously).toBe(true);
);

必须使用异步语义来解决同步测试的问题似乎很奇怪。

想知道是否有人提出了在 rxjs 中进行测试的良好解决方案,这将导致 expect 调用由测试库正确处理。

提前致谢!

package.json中的相关依赖:

 "dependencies": 
    "@babel/polyfill": "^7.0.0",
    "classnames": "^2.2.6",
    "history": "^4.7.2",
    "json-stringify-pretty-compact": "^1.2.0",
    "minimist": "^1.2.0",
    "normalize.css": "^8.0.0",
    "nullthrows": "^1.1.0",
    "react": "^16.5.2",
    "react-dom": "^16.5.2",
    "react-router-dom": "^4.3.1",
    "rxjs": "^6.3.3",
  ,
  "devDependencies": 
    "@babel/core": "^7.2.2",
    "@babel/plugin-proposal-class-properties": "^7.1.0",
    "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-flow": "^7.0.0",
    "@babel/preset-react": "^7.0.0",
    "babel-core": "^7.0.0-bridge.0",
    "babel-env": "^2.4.1",
    "babel-eslint": "^10.0.1",
    "babel-jest": "^23.6.0",
    "babel-loader": "^8.0.4",
    "copy-webpack-plugin": "^4.5.3",
    "css-loader": "^1.0.0",
    "eslint": "^5.9.0",
    "eslint-plugin-flowtype": "^3.2.0",
    "eslint-plugin-import": "^2.14.0",
    "eslint-plugin-react": "^7.11.1",
    "eslint-watch": "^4.0.2",
    "flow-bin": "^0.83.0",
    "html-webpack-plugin": "^3.2.0",
    "jest": "^23.6.0",
    "prettier": "^1.15.3",
    "style-loader": "^0.23.1",
    "webpack": "^4.20.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.9"
  

.babelrc 文件


  "plugins": [
    "module:@babel/plugin-proposal-class-properties",
    "module:@babel/plugin-proposal-object-rest-spread"
  ],
  "presets": [
    ["module:@babel/preset-env",  "targets":  "node": "6.10"  ],
    "module:@babel/preset-flow"
  ]

【问题讨论】:

【参考方案1】:

即使我在这个话题上几年后,这可能会帮助其他刚接触异步代码的人。 例如,请参考https://jestjs.io/docs/asynchronous 并在订阅结束时使用 done() 回调。 如果不执行此回调,由于之前的错误,测试将按预期失败。

it('should fetch the right data', done => 
  fetchData().subscribe(data => 
    expect(data).toBe('expected data');
    done();
  );
);

【讨论】:

【参考方案2】:

发现问题了!把这个留给遇到类似问题的人。 RxJS 和 Jest 工作正常并正确传播错误。问题是我在测试脚本中添加了一个“jest.useFakeTimers”调用。出于某种原因,这导致错误无法在测试中正确传播。我需要添加“jest.runAllTimers”来抛出错误。以下是正确实施的完整测试脚本:

import of from 'rxjs';

jest.useFakeTimers();

test('should fail', () => 
  of(false).subscribe(val => 
    expect(val).toBe(true);
  );
  jest.runAllTimers();
);

如果您不需要模拟计时器,则无需添加它们。我认为即使我可以验证代码是同步调用的,假计时器也是一个问题,这有点奇怪。如果有人对为什么会这样的实施细节有更深入的了解,我将不胜感激。

【讨论】:

subscribe 永远不会同步抛出。见:github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…

以上是关于开玩笑不处理 RxJS observable 的 subscribe() 中的 expect() 错误的主要内容,如果未能解决你的问题,请参考以下文章

Observable 错误,执行不同的 observable,重试原始 observable (Angular, RxJs6)

RxJS 等待订阅 Observable 完成

RxJS - 只订阅一次但不完成 Observable

Angular之Rxjs基础操作

Angular 6 / Rxjs - 如何基础:observables 成功,错误,最后

Rxjs 过滤器运算符不适用于 Angular2 Observable