とんかつ時々あんどーなつ

〜たとえ低空でも飛行していられるように〜

Angular アプリケーションのユニットテスト環境をゼロから作る

いつも ng new 一発でテスト環境まで準備済みだが、ふとユニットテスト環境を作るのにどういうパッケージとどういう設定が必要かきになったので試してみた記録

テスト環境がない状態で Angular アプリケーションを用意

  • ng new のオプションにめちゃくちゃ都合のいい minimal があったのでこれを使ってアプリケーションを用意
$ npx @angular/cli new ng-sample --minimal

? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? SCSS   [ https://sass-lang.com/documentation/syntax#scss                ]
CREATE ng-sample/README.md (1038 bytes)
CREATE ng-sample/.gitignore (631 bytes)
CREATE ng-sample/angular.json (3215 bytes)
CREATE ng-sample/package.json (880 bytes)
CREATE ng-sample/tsconfig.json (489 bytes)
CREATE ng-sample/browserslist (429 bytes)
CREATE ng-sample/tsconfig.app.json (210 bytes)
CREATE ng-sample/src/favicon.ico (948 bytes)
CREATE ng-sample/src/index.html (307 bytes)
CREATE ng-sample/src/main.ts (372 bytes)
CREATE ng-sample/src/polyfills.ts (2835 bytes)
CREATE ng-sample/src/styles.scss (80 bytes)
CREATE ng-sample/src/assets/.gitkeep (0 bytes)
CREATE ng-sample/src/environments/environment.prod.ts (51 bytes)
CREATE ng-sample/src/environments/environment.ts (662 bytes)
CREATE ng-sample/src/app/app.module.ts (314 bytes)
CREATE ng-sample/src/app/app.component.ts (1470 bytes)

テストに必要なパッケージのインストール

  • 通常 Angular アプリケーションのユニットテストには Jasmine と Karma が使われているので同じ環境を揃えにいく
  • Karma の Installation を参考にしてみると最低限必要なパッケージがわかる
$ yarn add jasmine-core karma karma-jasmine karma-chrome-launcher -D
  • テストの実行すると起動することがわかる
$ yarn karma start

yarn run v1.21.1
$ /Users/path/to/ng-sample/node_modules/.bin/karma start
12 06 2020 09:34:34.912:WARN [karma]: No captured browser, open http://localhost:9876/
12 06 2020 09:34:34.978:INFO [karma-server]: Karma v5.1.0 server started at http://0.0.0.0:9876/

設定ファイルを書く

  • Karm の init コマンドで conf ファイルを作る(すべて default で作成)
yarn karma init

yarn run v1.21.1
$ /Users/path/to/ng-sample/node_modules/.bin/karma init

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
> 

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> 

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
> 

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes


Config file generated at "/Users/path/to/ng-sample/karma.conf.js".
✨  Done in 33.58s.
  • 作成された karma.conf.js は以下のようになっている
// Karma configuration
// Generated on Wed Jun 10 2020 22:37:18 GMT+0900 (GMT+09:00)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [
    ],


    // list of files / patterns to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
  • ここでもう一度 start をすると Chrome が立ち上がりテストが実行される

Angular CLI 経由でテストを動かす

  • angular.json に test architect を追加する
    • builder には @angular-devkit/build-angular:karma を指定
    • options は angular_devkit/build_angular/src/karma/schema.json を見ながら省略できないパラメータを指定
      • 必須項目は angular_devkit/build_angular/src/karma/schema.json の required を見ると確認できる
      • 現時点では main, tsConfig, karmaConfig の 3 つ
diff --git a/angular.json b/angular.json
index 05240c6..d5132e9 100644
--- a/angular.json
+++ b/angular.json
@@ -93,6 +93,14 @@
             }
           }
         },
+        "test": {
+          "builder": "@angular-devkit/build-angular:karma",
+          "options": {
+            "main": "src/test.ts",
+            "tsConfig": "",
+            "karmaConfig": "karma.conf.js"
+          }
+        },
         "extract-i18n": {
           "builder": "@angular-devkit/build-angular:extract-i18n",
           "options": {
  • tsConfig は最低限 key だけあれば良い
  • karmaConfig にはすでに作成済みの karma.conf.js を指定
  • main(entry-point) として指定する src/test.ts を準備
    • 参考になる資料が見つからなかったので minimal でなければ生成されるファイルを用意
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

declare const require: {
  context(path: string, deep?: boolean, filter?: RegExp): {
    keys(): string[];
    <T>(id: string): T;
  };
};

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
  • angular-devkit/build-angular を使用することを karma.conf.js に明記
diff --git a/karma.conf.js b/karma.conf.js
index 27a1565..238b42e 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -8,7 +8,12 @@ module.exports = function (config) {
 
     // frameworks to use
     // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
-    frameworks: ['jasmine'],
+    frameworks: ['jasmine', '@angular-devkit/build-angular'],
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-chrome-launcher'),
+      require('@angular-devkit/build-angular/plugins/karma'),
+    ],
 
     // list of files / patterns to load in the browser
     files: [],
  • Angular アプリケーションのテストの実行に Zone が必要なので polyfill を読み込む
diff --git a/angular.json b/angular.json
index 6f4687e..e39fec0 100644
--- a/angular.json
+++ b/angular.json
@@ -98,7 +98,8 @@
           "options": {
             "main": "src/test.ts",
             "tsConfig": "",
-            "karmaConfig": "karma.conf.js"
+            "karmaConfig": "karma.conf.js",
+            "polyfills": "src/polyfills.ts"
           }
         },
         "extract-i18n": {
  • Jasmine 用の型定義が必要なので @types/jasmine をインストール
$ yarn add @types/jasmine -D

サンプルテストコードをおいて実行してみる

  • app.component.spec.ts を用意
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent],
    }).compileComponents();
  }));

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });
});
  • テストの実行
$ yarn test --watch false

yarn run v1.21.1
$ ng test --watch false
10% building 1/1 modules 0 active12 06 2020 13:00:14.871:INFO [karma-server]: Karma v5.1.0 server started at http://0.0.0.0:9876/
12 06 2020 13:00:14.873:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
12 06 2020 13:00:14.878:INFO [launcher]: Starting browser Chrome
12 06 2020 13:00:27.166:INFO [Chrome 83.0.4103.97 (Mac OS 10.15.5)]: Connected on socket lsZPFDaFH_rTC8F2AAAA with id 33650755
Chrome 83.0.4103.97 (Mac OS 10.15.5): Executed 1 of 1 SUCCESS (0.248 secs / 0.171 secs)
TOTAL: 1 SUCCESS
✨  Done in 33.15s.

テストが動いた👏

テスト用のパッケージを確認

  • minimal 指定しない場合にインストールされるテスト用のパッケージを確認

上でインストール済みのパッケージ

jasmine-core

karma

karma-jasmine

karma-chrome-launcher

上でインストールしていないパッケージ

jasmine-spec-reporter

karma-jasmine-html-reporter

karma-coverage-istanbul-reporter

protractor

感想

test.ts の役割がモヤッとしている…が、基本的には Karma の設定をするだけで構築できることがわかった。インストールされているパッケージの役割もわかったのがよい。