在 Angular 7 (Karma 2.0.4) 中成功完成的 Karma 测试在 Angular 8 (Karma 4.1.0) 升级后失败

ste*_*e_b 6 unit-testing jasmine karma-runner karma-jasmine angular-cli

我们有一套约 1100 个单元在 \xe2\x80\x99ng test\xe2\x80\x99 中运行,目前在 Angular 7.2.5 中在约 4 分钟内运行完成,没有出现任何故障,并且由于随机故障而无法运行完成,在 Angular 8.0.0 中,速度变慢,并在 4 分钟前断开连接。

\n\n

测试在 Chrome 或 ChromeHeadless 中的 Angular 7 中成功运行。

\n\n

已经尝试过:

\n\n
    \n
  1. 为了消除样式元素的已知内存泄漏,我们在此处实现了 \xe2\x80\x9cStyle Cleanup\xe2\x80\x9d :
  2. \n
\n\n

通过运行cleanStylesFromDOM1100afterAll个测试中的每个描述块。

\n\n
    \n
  1. 在 Angular 8 中,我们尝试将 Karma 从 4.1.0 回归到 3.0.0,将 jasmine-core 从 3.4.0 回归到 2.99.1,但没有成功。

  2. \n
  3. 尝试使用 Angular 8.1.1 和 Karma 4.1.0 和 jasmine-core 3.4.0,但没有成功。

  4. \n
  5. 增加了 Karma 超时:

  6. \n
\n\n
\n

browserNoActivityTimeout: 120000, captureTimeout: 60000,\n reportSlowerThan: 2000, browserDisconnectTolerance: 2,\n browserDisconnectTimeout: 20000, browserSocketTimeout: 20000,\n processKillTimeout: 20000

\n
\n\n
    \n
  1. 增加 Node.js 的内存--max_old_space_size=8192

  2. \n
  3. 关闭 \xe2\x80\x98ng test\xe2\x80\x99 源映射生成并观看

  4. \n
  5. 在 karma.conf.js 中关闭 Jasmine 随机:jasmine: {\xe2\x80\xa8\xc2\xa0 \nrandom: false,\xe2\x80\xa8\xc2\xa0 failFast: true,\xe2\x80\xa8\xc2\xa0 timeoutInterval: 1000\xe2\x80\xa8}

  6. \n
  7. 对于每个 \xe2\x80\x98describe\xe2\x80\x99 块中的每个 \xe2\x80\x98it\xe2\x80\x99 调用 afterEach 和fixture.destroy

  8. \n
  9. 尝试将 beforeEach TestBed 设置更改为 beforeAll,如下所示

    \n\n

    包.json

    \n\n
    {\n  "name": "myapp",\n  "version": "0.0.0",\n  "scripts": {\n    "ng": "ng",\n    "build": "ng build",\n    "test": "ng test",\n    "lint": "ng lint",\n    "e2e": "ng e2e"\n  },\n  "private": true,\n  "dependencies": {\n    "@angular/animations": "~8.0.0",\n    "@angular/common": "~8.0.0",\n    "@angular/compiler": "~8.0.0",\n    "@angular/core": "~8.0.0",\n    "@angular/forms": "~8.0.0",\n    "@angular/platform-browser": "~8.0.0",\n    "@angular/platform-browser-dynamic": "~8.0.0",\n    "@angular/router": "~8.0.0",\n    "@ng-bootstrap/ng-bootstrap": "^4.0.0",\n    "@ng-select/ng-select": "^2.20.0",\n    "@ngrx/effects": "^8.0.1",\n    "@ngrx/entity": "^8.0.1",\n    "@ngrx/router-store": "^8.0.1",\n    "@ngrx/store": "^8.0.1",\n    "@ngrx/store-devtools": "^8.0.1",\n    "@ngx-translate/core": "^11.0.1",\n    "angular-resizable-element": "^3.2.4",\n    "angular-split": "^3.0.1",\n    "bootstrap": "^4.1.3",\n    "core-js": "^2.6.9",\n    "jquery": "^3.3.1",\n    "jquery-ui": "^1.12.1",\n    "jquery-ui-bundle": "^1.11.4",\n    "jquery.fancytree": "^2.26.0",\n    "lodash": "^4.17.11",\n    "moment": "^2.17.1",\n    "ngx-infinite-scroll": "^7.2.0",\n    "ngx-nvd3": "^1.0.9",\n    "ngx-restangular": "^5.0.0-rc1",\n    "popper.js": "^1.15.0",\n    "rxjs": "~6.4.0",\n    "tslib": "^1.9.0",\n    "ui-contextmenu": "^1.18.1",\n    "urijs": "^1.18.6",\n    "zone.js": "~0.9.1"\n  },\n  "devDependencies": {\n    "@angular-devkit/build-angular": "~0.800.0",\n    "@angular/cli": "~8.0.2",\n    "@angular/compiler-cli": "~8.0.0",\n    "@angular/language-service": "~8.0.0",\n    "@types/jasmine": "~3.3.8",\n    "@types/jasminewd2": "~2.0.3",\n    "@types/jquery": "^3.3.29",\n    "@types/jquery.fancytree": "^2.7.32",\n    "@types/node": "^8.9.5",\n    "codelyzer": "^5.0.0",\n    "jasmine-core": "~3.4.0",\n    "jasmine-marbles": "^0.6.0",\n    "jasmine-spec-reporter": "~4.2.1",\n    "karma": "~4.1.0",\n    "karma-chrome-launcher": "~2.2.0",\n    "karma-coverage-istanbul-reporter": "~2.0.1",\n    "karma-jasmine": "~2.0.1",\n    "karma-jasmine-html-reporter": "^1.4.0",\n    "protractor": "~5.4.0",\n    "ts-mockito": "^2.3.1",\n    "ts-node": "~7.0.0",\n    "tslint": "~5.15.0",\n    "typescript": "~3.4.3",\n    "webpack": "^4.37.0"\n  }\n}\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    karma.conf.js

    \n\n
    module.exports = function (config) {\n  config.set({\n    basePath: \'\',\n    frameworks: [\'jasmine\', \'@angular-devkit/build-angular\'],\n    browsers: [\'ChromeHeadless\'],\n    plugins: [\n      require(\'karma-jasmine\'),\n      require(\'karma-chrome-launcher\'),\n      require(\'karma-jasmine-html-reporter\'),\n      require(\'karma-coverage-istanbul-reporter\'),\n      require(\'@angular-devkit/build-angular/plugins/karma\')\n    ],\n    client: {\n      clearContext: false // leave Jasmine Spec Runner output visible in browser\n    },\n    coverageIstanbulReporter: {\n      dir: require(\'path\').join(__dirname, \'./coverage/webr3\'),\n      reports: [\'html\', \'lcovonly\', \'text-summary\'],\n      fixWebpackSourcePaths: true\n    },\n    reporters: [\'progress\', \'kjhtml\'],\n    port: 9876,\n    colors: true,\n    logLevel: config.LOG_INFO,\n    autoWatch: true,\n    singleRun: false,\n    restartOnFileChange: true\n  });\n};\n
    Run Code Online (Sandbox Code Playgroud)
  10. \n
\n\n

测试示例:

\n\n
describe(\'PageNotFoundComponent\', () => {\n  let component: PageNotFoundComponent;\n  let fixture: ComponentFixture<PageNotFoundComponent>;\n  let selectedTextElement: HTMLElement;\n  let router;\n  let location;\n\n  beforeEach(async(() => {\n    TestBed.configureTestingModule({\n      imports: [\n        TranslateModule.forRoot({\n          loader: {\n            provide: TranslateLoader,\n            useClass: WebpackTranslateLoader\n          }\n        }),\n        RouterTestingModule.withRoutes(\n          [\n            {\n              path: \'basepath\',\n              redirectTo: \'nwi\'\n            },\n            {\n              path: \'**\',\n              component: PageNotFoundComponent\n            }\n          ]\n        ),\n      ],\n      declarations: [ PageNotFoundComponent ]\n    })\n    .compileComponents();\n  }));\n\n  afterAll(() => {\n    cleanStylesFromDOM();\n  });\n\n  beforeEach(() => {\n    fixture = TestBed.createComponent(PageNotFoundComponent);\n    component = fixture.componentInstance;\n    fixture.detectChanges();\n    router = TestBed.get(Router);\n    location = TestBed.get(Location);\n  });\n\n  it(\'should show 404 text\', fakeAsync(() => {\n    const navigationExtras: NavigationExtras = {\n      queryParams: {}\n    };\n    router.navigate([`/unknown`], navigationExtras);\n    tick();\n    expect(decodeURI(location.path())).toBe(`/unknown`);\n    const textElement: HTMLElement = fixture.nativeElement;\n    selectedTextElement = textElement.querySelector(\'p\');\n    expect(selectedTextElement.innerText).toEqual(\'404\');\n  }));\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们预计约 1100 个测试能够在 Angular 8 中顺利运行完成,不会像在 Angular 7 中那样失败。

\n\n

以下是遇到的不应该发生的故障:

\n\n
\n

Chrome 75.0.3770 (Mac OS X 10.13.6) VMComponent 应验证 Vul\n 抑制获取是否正确呈现 FAILED\n TypeError: 无法在 UserContext 的 \n 处读取 null 的属性 \'className\'。( http://localhost:9876/_karma_webpack_/webpack:/src/app/components/vm-disabled-risk-radius/vm-disabled-risk-radius.component.spec.ts:72:18 )\n 在 ZoneDelegate .invoke ( http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-evergreen.js:359:1 )\n 在 ProxyZoneSpec.onInvoke ( http://localhost:9876/_karma_webpack_ /webpack:/node_modules/zone.js/dist/zone-testing.js:308:1 )\n 在 ZoneDelegate.invoke ( http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/ zone-evergreen.js:358:1 )\n 在 Zone.run ( http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-evergreen.js:124:1 )\n在 runInTestZone ( http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:561:1 )\n 在 UserContext 处。( http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:576:1 )\n 位于

\n\n

...以及另外 20 次随机失败\n并最终断开连接:

\n\n

24 07 2019 12:30:11.055:警告 [Chrome 75.0.3770 (Mac OS X 10.13.6)]: 在 2Chrome 75.0.3770 (Mac OS X 10.13.6) 超时之前断开连接(0 次)重新连接失败错误\n在超时 2000 毫秒之前断开连接重新连接失败(传输错误)\n Chrome 75.0.3770 (Mac OS X 10.13.6): 已执行 582 个,共 1134 个(20 个失败)(跳过 3 个)已断开连接(4 分钟 7.005 秒/3 分钟 53.442\n秒)

\n
\n

Kla*_*r_1 1

不确定cleanStylesFromDOM你提到的是什么,但最近我在 1800 测试大型套件中遇到了类似的问题。症状相似:

  • 在运行结束时测试超时:
FAILED
Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)
at <Jasmine>
Run Code Online (Sandbox Code Playgroud)
  • 在资源受限的环境(即 CI)中运行永远不稳定。

<style>性能分析表明,每次测试后,标签数量<head>稳步增加。团队不确定这是否是原因,但我们决定将次优资源管理作为主要嫌疑人,类似于您的cleanStylesFromDOM. 幸运的是,去年这个 PR被合并了,所以不再afterEach需要额外的调用。要在每次测试后强制 Angular 清理,请更新“test.ts”中的测试床初始化,如下所示:

FAILED
Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)
at <Jasmine>
Run Code Online (Sandbox Code Playgroud)

{teardown: {destroyAfterEach: true}}是兴趣线。不幸的是,文档并没有对destroyAfterEach. 对我来说,打开该设置最终会带来稳定、始终绿色的运行:

  1. 它揭示了测试中的错误,这些错误在各个测试期间被资源重用所掩盖。我能够在本地重现并修复大量不稳定测试的实例。
  2. 不再有超时,<style>s 被清理。资源消耗下降。
  3. CI 中不稳定的测试集变得稳定,我能够禁用这些测试以便稍后解决它们。