Angularプロジェクトでgitのコミット前に構文チェックとテストを実行する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Angular CLIでプロジェクトを作成すると始めから構文チェック(ng lint)とテスト(ng test)が実行できるようになっていますが、 gitにコミットする前に構文チェックとテストを自動で実行する設定をしてみました。

準備

設定対象のAngularプロジェクトをQuickStart通りに作成します。

$ ng new my-app
$ cd my-app

pre-commitのインストール

gitのpre-commit hookを利用するために開発用のモジュールとしてpre-commitをインストールします。

$ npm install --save-dev pre-commit

構文チェックを実行させる

まずは構文チェックを実行するようにpackage.jsonに以下の内容を追記します。

  "pre-commit": [
    "lint"
  ]

動作確認のためにわざと構文チェックでエラーになるようにapp.component.tsをちょっと修正します。 (デフォルトの設定では文字列はシングルクォートで囲む必要があります。)

変更前

title = 'my-app';

変更後

title = "my-app";

コミットしてみます。

$ git add src/app/app.component.ts
$ git commit -m'ダブルクォートにしてみたよ'
ERROR: /Users/kubota.daisuke/devel/angular/my-app/src/app/app.component.ts[9, 11]: " should be '

Lint errors found in the listed files.

構文チェックでエラーになりコミットできないようになりました。

構文チェックは動作するようになったので、先ほど修正したコードをシングルクォートに戻してコミットしておきます。

テストを実行させる

続いてテストも実行させるようにpackage.jsonを以下のように変更します。

  "pre-commit": [
    "lint",
    "test"
  ]

こちらもわざとテストでエラーになるようにapp.component.spec.tsを以下のように変更します。

変更前

expect(app.title).toEqual('my-app');

変更後

expect(app.title).toEqual('mysterious-app');

コミットしてみます。

$ git add src/app/app.component.spec.ts 
$ git commit -m'テストも実行するようにしてみたよ'
All files pass linting.


All files pass linting.
 10% building modules 1/1 modules 0 active24 08 2018 17:43:25.918:WARN [karma]: No captured browser, open http://localhost:9876/
24 08 2018 17:43:25.924:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
24 08 2018 17:43:25.925:INFO [launcher]: Launching browser Chrome with unlimited concurrency
24 08 2018 17:43:25.931:INFO [launcher]: Starting browser Chrome
24 08 2018 17:43:30.374:WARN [karma]: No captured browser, open http://localhost:9876/
24 08 2018 17:43:30.545:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket mcdvUugXHusbm6PyAAAA with id 36584294
Chrome 68.0.3440 (Mac OS X 10.13.6) AppComponent should have as title 'my-app' FAILED
        Expected 'my-app' to equal 'mysterious-app'.
            at UserContext.eval (webpack:///./src/app/app.component.spec.ts?:22:27)
            at ZoneDelegate.invoke (webpack:///./node_modules/zone.js/dist/zone.js?:387:26)
            at AsyncTestZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:712:39)
            at ProxyZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:284:39)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 2 of 3 (1 FAILED) (0 secs / 0.141 secs)
Chrome 68.0.3440 (Mac OS X 10.13.6) AppComponent should have as title 'my-app' FAILED
        Expected 'my-app' to equal 'mysterious-app'.
            at UserContext.eval (webpack:///./src/app/app.component.spec.ts?:22:27)
            at ZoneDelegate.invoke (webpack:///./node_modules/zone.js/dist/zone.js?:387:26)
            at AsyncTestZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:712:39)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 3 of 3 (1 FAILED) (0.231 secs / 0.192 secs)

一応テストも動作するようになりましたが、ng testはデフォルトではファイルの変更を待ち続ける状態になっているため、このままではテストが成功しても失敗してもコミットができません。

ng testのヘルプを確認すると、--watchオプションというものがあるので、このオプションを使えばファイルの変更検知を無効にできそうです。

$ ng test -h
   
  --watch
    Run build when files change.

package.jsonのscriptsにng testの設定があるので、ここに--watch=falseを追加してみます。

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test --watch=false",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },

もう一度コミットを試みます。

$ git commit -m'テストも実行するようにしてみたよ'
All files pass linting.


All files pass linting.
 10% building modules 1/1 modules 0 active24 08 2018 17:49:23.742:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
24 08 2018 17:49:23.743:INFO [launcher]: Launching browser Chrome with unlimited concurrency
24 08 2018 17:49:23.750:INFO [launcher]: Starting browser Chrome
24 08 2018 17:49:28.364:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket E-kVNTX9yZUvjZvyAAAA with id 54505362
Chrome 68.0.3440 (Mac OS X 10.13.6) AppComponent should have as title 'my-app' FAILED
        Expected 'my-app' to equal 'mysterious-app'.
            at UserContext.eval (webpack:///./src/app/app.component.spec.ts?:22:27)
            at ZoneDelegate.invoke (webpack:///./node_modules/zone.js/dist/zone.js?:387:26)
            at AsyncTestZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:712:39)
            at ProxyZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:284:39)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 2 of 3 (1 FAILED) (0 secs / 0.14 secs)
Chrome 68.0.3440 (Mac OS X 10.13.6) AppComponent should have as title 'my-app' FAILED
        Expected 'my-app' to equal 'mysterious-app'.
            at UserContext.eval (webpack:///./src/app/app.component.spec.ts?:22:27)
            at ZoneDelegate.invoke (webpack:///./node_modules/zone.js/dist/zone.js?:387:26)
            at AsyncTestZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:712:39)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 3 of 3 (1 FAILED) (0.237 secs / 0.198 secs)
pre-commit:
pre-commit: We've failed to pass the specified git pre-commit hooks as the `test`
pre-commit: hook returned an exit code (1). If you're feeling adventurous you can
pre-commit: skip the git pre-commit hooks by adding the following flags to your commit:
pre-commit:
pre-commit:   git commit -n (or --no-verify)
pre-commit:
pre-commit: This is ill-advised since the commit is broken.
pre-commit:

ファイルの変更検知が無効になり期待した挙動になりました。

ただしこの方法では通常の開発時もファイルの変更検知が無効になってしまうため、先ほど修正したscriptsのtestの部分は元に戻して、pre-commit時だけファイルの変更検知を無効にしたいと思います。

package.jsonのscriptsにpre-commit-testを追加します。

scriptsの中はこんな感じになります。

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "pre-commit-test": "ng test --watch=false",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },

続いてpre-commitの設定箇所もpre-commit-testを実行するように変更します。

  "pre-commit": [
    "lint",
    "pre-commit-test"
  ]

コミットして動作を確認してみましょう。

$ git commit -m'テストも実行するようにしてみたよ'

うまくいきました!ファイルの変更検知が無効になっています。

All files pass linting.


All files pass linting.
 10% building modules 1/1 modules 0 active24 08 2018 20:06:05.669:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
24 08 2018 20:06:05.671:INFO [launcher]: Launching browser Chrome with unlimited concurrency
24 08 2018 20:06:05.676:INFO [launcher]: Starting browser Chrome
24 08 2018 20:06:10.248:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket ovLuICczAE9B-FZuAAAA with id 43389736
Chrome 68.0.3440 (Mac OS X 10.13.6) AppComponent should have as title 'my-app' FAILED
        Expected 'my-app' to equal 'mysterious-app'.
            at UserContext.eval (webpack:///./src/app/app.component.spec.ts?:22:27)
            at ZoneDelegate.invoke (webpack:///./node_modules/zone.js/dist/zone.js?:387:26)
            at AsyncTestZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:712:39)
            at ProxyZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:284:39)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 2 of 3 (1 FAILED) (0 secs / 0.132 secs)
Chrome 68.0.3440 (Mac OS X 10.13.6) AppComponent should have as title 'my-app' FAILED
        Expected 'my-app' to equal 'mysterious-app'.
            at UserContext.eval (webpack:///./src/app/app.component.spec.ts?:22:27)
            at ZoneDelegate.invoke (webpack:///./node_modules/zone.js/dist/zone.js?:387:26)
            at AsyncTestZoneSpec.onInvoke (webpack:///./node_modules/zone.js/dist/zone-testing.js?:712:39)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 3 of 3 (1 FAILED) (0.219 secs / 0.187 secs)
pre-commit:
pre-commit: We've failed to pass the specified git pre-commit hooks as the `pre-commit-test`
pre-commit: hook returned an exit code (1). If you're feeling adventurous you can
pre-commit: skip the git pre-commit hooks by adding the following flags to your commit:
pre-commit:
pre-commit:   git commit -n (or --no-verify)
pre-commit:
pre-commit: This is ill-advised since the commit is broken.
pre-commit:

最後に修正したテストを元に戻してコミットできるか確認しましょう。

$ git commit -m'テストも実行するようにしてみたよ'

無事コミットできたようです。

All files pass linting.


All files pass linting.
 10% building modules 1/1 modules 0 active24 08 2018 20:07:32.231:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/
24 08 2018 20:07:32.232:INFO [launcher]: Launching browser Chrome with unlimited concurrency
24 08 2018 20:07:32.239:INFO [launcher]: Starting browser Chrome
24 08 2018 20:07:37.098:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket 6roPQhnvqzjsWxxHAAAA with id 22863771
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 3 of 3 SUCCESS (0.232 secs / 0.207 secs)
[master ba7daa5] テストも実行するようにしてみたよ
 2 files changed, 7 insertions(+), 2 deletions(-)

まとめ

テスト時のファイルの変更検知を無効にするところで若干ハマりましたが、pre-commitモジュールのインストールとpackage.jsonのわずかな修正で目的を達成することができました。

この方法で構文チェックとテストを行わずにコミットしてしまいCIでコケるという悲しい思いをする方が(私も含め)ひとりでも減れば幸いです。