Dockerコンテナ上のAngular開発においてng test実行時に発生するエラーの対処方法

## きっかけ

> ng testコマンドはアプリケーションをウォッチモードでビルドし、Karmaテストランナーを起動します。
> [Angular 日本語ドキュメンテーション](https://angular.jp/guide/testing)

とありますが、Dockerコンテナ上のAngularで`ng test`コマンドを実行するとエラーが発生します。

```bash
# テストコマンド実行
ng test

# 以下のエラーが発生
✔ Browser application bundle generation complete.
17 03 2022 05:50:47.581:WARN [karma]: No captured browser, open http://localhost:9876/
17 03 2022 05:50:48.164:INFO [karma-server]: Karma v6.3.17 server started at http://localhost:9876/
17 03 2022 05:50:48.165:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
17 03 2022 05:50:48.212:INFO [launcher]: Starting browser Chrome
17 03 2022 05:50:48.213:ERROR [launcher]: No binary for Chrome browser on your platform.
  Please, set "CHROME_BIN" env variable.
```

## 目標
Dockerコンテナ上のAngular開発でwathcモードが有効なユニットテストが実行できるようにします。

## 注意
- 目標に記した通りの挙動になりますが、当方の理解不足のため、理屈を説明できない箇所があります。
- テストコードのコーディングの仕方は解説していません。
    当方ユニットテストの仕方を覚えようと`ng test`実行したところでエラー発生し、その対処を調べ、何とかクリアできたので、もし同様の箇所で詰まっておられる方の助けになればと思い当記事を投稿しました。
- 取り急ぎ当記事の結果をご覧になられたい方は、"まとめ"の節をご覧ください。

## 環境
- Windows10 Home Version:21H2
- WSL2
- docker desktop Version:4.6.0 (75818)

## 実践
### Dockerコンテナ上に環境構築

ホストPCでPowershellを起動し以下のコマンドを実行します。
```powershell
# node.jsのDockerイメージを取得する。
docker pull node:14

# カレントディレクトリを取得
$currentdir=$(Covert-Path .)

# コンテナ作成
# ※-vオプションの${currentdir}上のコマンドで取得したカレントディレクトリを使用している。
docker run --name angular-test -p 4200:4200 -v ${currentdir}:/app -w /app -it node:14 /bin/bash
```

以下はコンテナ内での作業です。
angularプロジェクトを生成します。
```bash
# angular/cliをインストールする。
npm install -g @angular/cli

# angularプロジェクトを生成する。
ng new ng-unit-test --directory=./ --routing=false --style=css

# 確認
# ホストPCでブラウザを開きlocalhost:4200にアクセスし画面が表示されることを確認
ng serve --host=0.0.0.0 --poll=2000
```

※Dokcerコンテナ上のAngularは、単に`ng serve`コマンドだとホストPCでブラウザでlocalhost:4200にアクセスできません。`--host`オプションを指定します。`--poll`オプションは変更を検知する時間をミリ秒単位で指定しています。この指定が無いと、Docker上のAngularは`ng serve`時、コードを変更しても変更を検出しません。
(参考)[VM上でng serveするときのメモ - 備忘69](https://shinchang69.hatenablog.com/entry/2018/02/10/223736)


この段階で`ng test`を実行するとエラーが発生します。


### ng test実行時のエラーに対処
1. Dockerコンテナ上にChromiumをインストール
    エラーの原因はDockerコンテナ上にブラウザがないことにより発生しているので、Chromiumブラウザをインストールします。
    
    ```bash
    apt-get update
    apt-get upgrade
    
    # --no-install-recommendsオプションにより、関連する必須ではないパッケージのインストールを除外する。
    apt-get install --no-install-recommends chromium
    ```

1. karma.conf.jsを調整する
    
    ```javascript:karma.conf.js
    // Karma configuration file, see link for more information
    // https://karma-runner.github.io/1.0/config/configuration-file.html
    // Chromeの環境変数をセットする。
    process.env.CHROME_BIN="/usr/bin/chromium";//←追記
    
    module.exports = function (config) {
    // ...中略...
      //↓変更
      //browsers: ['Chrome'],
      browsers: ['ChromeHeadless'],
      //↑変更
      
      //↓追記
      customLaunchers: {
        ChromeHeadless: {
            base: 'Chromium', //←Chromiumの接頭は大文字である。
            flags: [
              '--no-sandbox',
              '--headless',
              '--disable-gpu',
              '--remote-debugging-port=9222'
            ]
          }
      }, 
      //↑追記
    ```

1. テスト実行
    ```bash
    ✔ Browser application bundle generation complete.
    18 03 2022 01:50:04.542:WARN [karma]: No captured browser, open http://localhost:9876/
    18 03 2022 01:50:05.185:INFO [karma-server]: Karma v6.3.17 server started at http://localhost:9876/
    18 03 2022 01:50:05.185:INFO [launcher]: Launching browsers ChromeHeadless with concurrency unlimited
    18 03 2022 01:50:05.227:INFO [launcher]: Starting browser Chromium
    18 03 2022 01:50:19.964:INFO [Chrome Headless 73.0.3683.75 (Linux x86_64)]: Connected on socket G6Hm62Q0vFRxdfamAAAB with id 10245111
    Chrome Headless 73.0.3683.75 (Linux x86_64): Executed 3 of 3 SUCCESS (0.408 secs / 0.293 secs)
    TOTAL: 3 SUCCESS
    ```
    
    エラーなくテストが実行できました。
    ホストPCでブラウザでlocalhost:9876にアクセスし、ブラウザ上でテスト状況を確認することもできました。
    話はそれますが、コンテナ生成時 `-p`オプションでポート9876を指定していないのにホストPCからlocalhost:9876にアクセスできるのか少し疑問に感じております。
    
    話を戻しまして、Angular公式サイトのテストの章に以下のようにあります。
    >このアクションを確認するためにapp.component.tsに小さな変更を加えて保存してみましょう。 テストが再び実行され、ブラウザが更新されます。そして新しいテストの結果が表示されます。
    >[Angular 日本語ドキュメンテーション](https://angular.jp/guide/testing)
    
    小さな変更を加えて保存してみました。
    変更が検知されテストが再実行されることを期待したのですが、されませんでした。
    karma.conf.jsで以下の設定をしてあるのでウォッチモードが有効のはずです。
    ```javascript:karma.conf.js
    autoWatch: true,
    ```
    
### ウォッチモードを働かせる
Angular公式のng testを参照したところ、--pollオプションがあることに気づきました。

> --poll 
> Enable and define the file watching poll time period in milliseconds
> (google翻訳)
> ファイル監視ポーリング期間を有効にして、ミリ秒単位で定義します。
> [Angular 日本語ドキュメンテーション](https://angular.jp/cli/test)

そういえばDokcerコンテナ上のAngularでng serveするとき--pollオプションを指定していた事に気づき、ng test時も同様に指定します。

```bash
ng test --poll=2000
```

コードに変更を加えたところ、期待した通りテストが再実行されました。


## まとめ
期待した挙動にするための手順を再掲します。

1. コンテナ上にChromiumをインストールする

    ```bash
    apt-get update
    apt-get upgrade
    apt-get install --no-install-recommends chromium
    ```
    
1. karma.conf.jsを調整する

    ```javascript:karma.conf.js
    // Karma configuration file, see link for more information
    // https://karma-runner.github.io/1.0/config/configuration-file.html
    // Chromeの環境変数をセットする。
    process.env.CHROME_BIN="/usr/bin/chromium";//←追記
    
    module.exports = function (config) {
    // ...中略...
      //↓変更
      //browsers: ['Chrome'],
      browsers: ['ChromeHeadless'],
      //↑変更
      
      //↓追記
      customLaunchers: {
        ChromeHeadless: {
            base: 'Chromium', //←Chromiumの接頭は大文字である。
            flags: [
              '--no-sandbox',
              '--headless',
              '--disable-gpu',
              '--remote-debugging-port=9222'
            ]
          }
      }, 
      //↑追記
    ```

1. `--poll`オプションを指定して`ng test`を実行
    ```bash
    ng test --poll=2000
    ```


以上です。

## 参考にさせていただいたサイト、記事
- [VM上でng serveするときのメモ - 備忘69](https://shinchang69.hatenablog.com/entry/2018/02/10/223736)
- [AngularのテストをDocker(CentOS 7)上で回すときに設定したこと - Qiita](https://qiita.com/kawakawaryuryu/items/f4f9149179aa5b0c12fe)
- [GitHub - geoffroyvergne/docker-angular-test-suite: Docker image to test (unit and e2e) angular appliaction](https://github.com/geoffroyvergne/docker-angular-test-suite)

ありがとうございました。