Closed11

AngularのテストをCloud Buildで回す

このスクラップについて

Angularの自動テストをCloud Buildで動かすところまで持っていく。

Angularといっているが、結局はCloud Build環境での依存関係(karmaなど)をうまく解決するためのあれやこれなので、実際はAngularとはあまり関係ない。

nodeのバージョンとかは特に明記しない。

もし、上手い感じにまとまれば記事にしたいなあ。

ng new 直後の状況。
こいつを自動テストできる状態にする。

package.json
{
  "name": "test-cloud-build",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~11.2.8",
    "@angular/common": "~11.2.8",
    "@angular/compiler": "~11.2.8",
    "@angular/core": "~11.2.8",
    "@angular/forms": "~11.2.8",
    "@angular/platform-browser": "~11.2.8",
    "@angular/platform-browser-dynamic": "~11.2.8",
    "@angular/router": "~11.2.8",
    "rxjs": "~6.6.0",
    "tslib": "^2.0.0",
    "zone.js": "~0.11.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.1102.7",
    "@angular/cli": "~11.2.7",
    "@angular/compiler-cli": "~11.2.8",
    "@types/jasmine": "~3.6.0",
    "@types/node": "^12.11.1",
    "codelyzer": "^6.0.0",
    "jasmine-core": "~3.6.0",
    "jasmine-spec-reporter": "~5.0.0",
    "karma": "~6.1.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.0.3",
    "karma-jasmine": "~4.0.0",
    "karma-jasmine-html-reporter": "^1.5.0",
    "protractor": "~7.0.0",
    "ts-node": "~8.3.0",
    "tslint": "~6.1.0",
    "typescript": "~4.1.5"
  }
}

準備

CHROME_BINを通すためにpuppeteerを入れる

npm i -D puppeteer

設定ファイル達

karma.conf.js
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
+ if (require('puppeteer').executablePath()) {
+   process.env.CHROME_BIN = require('puppeteer').executablePath()
+ }

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      jasmine: {
        // you can add configuration options for Jasmine here
        // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
        // for example, you can disable the random execution with `random: false`
        // or set a specific seed with `seed: 4321`
      },
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    jasmineHtmlReporter: {
      suppressAll: true // removes the duplicated traces
    },
    coverageReporter: {
      dir: require('path').join(__dirname, './coverage/test-cloud-build'),
      subdir: '.',
      reporters: [
        { type: 'html' },
        { type: 'text-summary' }
      ]
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
+     customLaunchers: {
+       ChromeHeadlessCI: {
+         base: 'ChromeHeadless',
+         flags: ['--no-sandbox']
+       }
+     },
    singleRun: false,
    restartOnFileChange: true
  });
};
Dockerfile
FROM node

RUN apt-get update && \
    apt-get install -y libgtk-3.0 libgbm-dev libnss3 libatk-bridge2.0-0 libasound2

Docker imageはArtifact Registryにpushしておく。

cloudbuild.yaml
steps:
- name: node:alpine
  entrypoint: npm
  args: ['install']

- name: 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}'
  entrypoint: npm
  args: ["run", "test", "--" , "--no-watch", "--no-progress", "--browsers=ChromeHeadlessCI"]

timeout: 1500s

自前のコンテナを指定する際は、変数を使うと良さげだった。
TAGまで変数化するかはお好み。

Please, set "CHROME_BIN" env variable. エラー

ただ走らせるだけだと、こんなエラーが出る。

07 04 2021 07:00:14.287:INFO [launcher]: Launching browsers ChromeHeadlessCI with concurrency unlimited
07 04 2021 07:00:14.291:INFO [launcher]: Starting browser ChromeHeadless
07 04 2021 07:00:14.292:ERROR [launcher]: No binary for ChromeHeadless browser on your platform.
  Please, set "CHROME_BIN" env variable.

https://github.com/karma-runner/karma-chrome-launcher#headless-chromium-with-puppeteer

こちらを参考にて、karma.conf.js に process.env.CHROME_BIN = require('puppeteer').executablePath() を追加しておく。

依存ライブラリエラー

CHROME_BINを通した後にこんなエラーが出る。

07 04 2021 07:48:50.218:ERROR [launcher]: Cannot start ChromeHeadless
  /workspace/node_modules/puppeteer/.local-chromium/linux-856583/chrome-linux/chrome: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory

依存関係を解決した環境でテストを実行する。
Dockerfile参照のこと。

puppeteerを入れたくないver

cloudbuild.yaml
steps:
- name: node:alpine
  entrypoint: npm
  args: ['i', '-D', 'puppeteer']

- name: node:alpine
  entrypoint: npm
  args: ['install']

- name: 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}'
  entrypoint: npm
  args: ["run", "test", "--" , "--no-watch", "--no-progress", "--browsers=ChromeHeadlessCI"]

timeout: 1500s
Dockerfile
FROM node

RUN apt-get update && \
    apt-get install -y libgtk-3.0 libgbm-dev libnss3 libatk-bridge2.0-0 libasound2

# 自動で取得できるいい案ないかなあ
ENV CHROME_BIN=/workspace/node_modules/puppeteer/.local-chromium/linux-856583/chrome-linux/chrome

Error: Cannot find module '/workspace/npm

こうやって指定して実行していて、ハマった

- name: 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}'
  args:
  - npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI
node:internal/modules/cjs/loader:927
  throw err;
  ^

これで動く

- name: 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}'
  entrypoint: npm
  args: ["run", "test", "--" , "--no-watch", "--no-progress", "--browsers=ChromeHeadlessCI"]

which npm の結果が /usr/local/bin/npm だったため、これを指定しても動きそう

node-sassのエラー

環境が若干古い場合に出たりする

Step #2: ERROR in ./src/styles.scss (./node_modules/@angular-devkit/build-angular/src/angular-cli-files/plugins/raw-css-loader.js!./node_modules/postcss-loader/src??embedded!./node_modules/sass-loader/lib/loader.js??ref--14-3!./src/styles.scss)
Step #2: Module build failed (from ./node_modules/sass-loader/lib/loader.js):
Step #2: Error: Node Sass does not yet support your current environment: Linux 64-bit with Unsupported runtime (88)
Step #2: For more information on which environments are supported please see:
Step #2: https://github.com/sass/node-sass/releases/tag/v4.11.0
Step #2:     at module.exports (/workspace/node_modules/node-sass/lib/binding.js:13:13)
Step #2:     at Object.<anonymous> (/workspace/node_modules/node-sass/lib/index.js:14:35)
Step #2:     at Module._compile (node:internal/modules/cjs/loader:1092:14)
Step #2:     at Object.Module._extensions..js (node:internal/modules/cjs/loader:1121:10)
Step #2:     at Module.load (node:internal/modules/cjs/loader:972:32)
Step #2:     at Function.Module._load (node:internal/modules/cjs/loader:813:14)
Step #2:     at Module.require (node:internal/modules/cjs/loader:996:19)
Step #2:     at require (node:internal/modules/cjs/helpers:92:18)
Step #2:     at Object.sassLoader (/workspace/node_modules/sass-loader/lib/loader.js:46:72)
Step #2:  @ ./src/styles.scss 2:14-241
Step #2:  @ multi ./src/styles.scss

ここに書いてある、nodeのバージョンを指定する。

https://github.com/sass/node-sass/releases/tag/v4.11.0

FROM node:10-slim

Error: Missing binding /workspace/node_modules/node-sass/vendor/linux-x64-64/binding.node

こんな感じのエラーが出た

This usually happens because your environment has changed since running `npm install`.
Run `npm rebuild node-sass` to download the binding for your current environment.
    at module.exports (/workspace/node_modules/node-sass/lib/binding.js:15:13)
    at Object.<anonymous> (/workspace/node_modules/node-sass/lib/index.js:14:35)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at Object.sassLoader (/workspace/node_modules/sass-loader/lib/loader.js:46:72)
 @ ./src/app/assignment/assignment-add/assignment-add.component.ts 495:21-63
 @ ./src/app/assignment/assignment-add/assignment-add.component.spec.ts
 @ ./src sync \.spec\.ts$
 @ ./src/test.ts

こんな感じで対処した

- name: 'us-central1-docker.pkg.dev/$PROJECT_ID/${_REPOSITORY}/${_IMAGE}'
  entrypoint: 'npm'
  args: ['rebuild', 'node-sass']

Disconnectedreconnect failed before timeout of 2000ms

19 05 2021 02:10:08.711:WARN [HeadlessChrome 91.0.4469 (Linux 0.0.0)]: Disconnected (0 times)reconnect failed before timeout of 2000ms (transport close)
HeadlessChrome 91.0.4469 (Linux 0.0.0) ERROR
  Disconnectedreconnect failed before timeout of 2000ms (transport close)
HeadlessChrome 91.0.4469 (Linux 0.0.0): Executed 298 of 496 (skipped 140) DISCONNECTED (6 mins 41.202 secs / 6 mins 34.893 secs)
HeadlessChrome 91.0.4469 (Linux 0.0.0) ERROR
  Disconnectedreconnect failed before timeout of 2000ms (transport close)
HeadlessChrome 91.0.4469 (Linux 0.0.0): Executed 298 of 496 (skipped 140) DISCONNECTED (6 mins 41.202 secs / 6 mins 34.893 secs)
19 05 2021 02:10:08.925:ERROR [karma-server]: UncaughtException
npm ERR! code ELIFECYCLE
karma.conf.js
        flags: [ 
+         '--disable-gpu',
          '--no-sandbox'
        ]
...

+   singleRun: true,
+   restartOnFileChange: true,
+   browserDisconnectTolerance: 2,
+   captureTimeout: 600000,
+   browserNoActivityTimeout: 120000,
cloudbuild.yaml
  args: ["run", "test", "--" , "--no-watch", "--no-progress", "--browsers=ChromeHeadlessCI",  "--source-map=false"]

これで今の所問題なさそう

めちゃくちゃ遅いのでオススメはしないかも・・

パフォーマンス改善する余裕があったら再開するかもしれない

このスクラップは2021/09/10にクローズされました
ログインするとコメントできます