angular.jsのチュートリアルにはテストもきちんと含まれているが、キューピー3分間クッキングなみに出来上がった状態で提示されてくる。そのため、なんとなくわかった気になるが実際よく分かっていないという状態に陥りがちである。

どうなっているかをすこしは理解したいと思い、angular.jsでのテスト環境を一から構築してみる。

ユニットテスト Karma

ユニットテストはKarmaを使うのが楽そうである。公式のチュートリアルなどもKarmaが前提になっている。まあ、Karma自体が「angularのために」からスタートしているので相性がいい、かつ情報が豊富なのは当然なので使わない手はないと思う。

ここでは、Karma・mocha・chaiの構成を取る。基本的な設定は以前のポストをご参考頂ければ。CLIが分離したKarma0.12でmocha,chaiの環境を整える | niwaringo() {Tumblr}

angular.js特有の注意点は以下。

angular-mocks.jsのインストール

依存解決等のためにangular-mocks.jsが必要になる。bowerなどを使って事前にインストールしておく。

$ bower install angular-mocks -D

karma.conf.jsの注意点

angular.jsを一番最初に読み込む

bower_components以下全てのJSファイル読み込みなどとしておくと、angularが読み込む前にangular-mocksが読み込まれてエラーになるという悲しい自体に遭遇したりする。そのため一番はじめにangular.jsを読み込んでおく。

files: [
  'bower_components/angular/angular.js',
  'bower_components/**/*.js',
  'app/js/*.js',
  'test/unit/**/*_spec.js'
],

minファイルの除外

ちなみにbowerでインストールした場合は、minファイルも一緒にインストールされる事が多いためminファイルは除外しておくと良さそうである。angular特有ではないが、、、、

exclude: [
  'bower_components/**/*.min.js'
],

テストファイルでの注意点

angularはDIが非常に特徴的である。テストの時にコレを解決するためにmoduleでテストしたいモジュールを呼び出して、injectを使って依存を解決するという形を取る。moduleinjectはangular-mocksがグローバルに追加される。

describe('PhoneListCtrl', function(){
  beforeEach(module('phonecatApp'));
  it('should create "phones" model with 3 phones', inject(function($controller) {
    var scope = {},
        ctrl = $controller('PhoneListCtrl', {$scope:scope});
    expect(scope.phones).to.have.length(3);
  }));
});

上記テストコードはチュートリアルで紹介されているコード。(expectはchaiにしていますが)

ユニットテストはangular-mocksを使って依存を解決すれば基本問題はなさそうである。angular-mocksは他にも色々便利そうなのできちんと調べたほうが良さそう……そのうち。

E2Eテスト {protractor}

E2Eテストは、angular.jsのために開発された「protractor」というテストフレームワークがある。昔はKarma自体にE2Eの仕組みが組み込まれていたが分離されて、ユニットテストはKarma、E2Eテストはprotractorという役割分担になっているようである。protractorは現在かなり活発に開発が進められているので「あれ?」と思ったらアップデートすると解決することが多い :P

protractorのインストール

公式ではグローバルになっているが、とりあえずはローカルで様子見を。

$ npm install protractor -D

standalone selenium server と chromedriverのダウンロード

protractorはWebDriverJSが土台になっているため、seleniumのstandalone severが必要になる。(chromeだけでテストする場合はchromedriverだけでも良い)

以下のコマンドでselenium-server-standaloneとchromedriverの両方をダウンロードしてくれる。

./node_modules/protractor/bin/webdriver-manager update 

設定ファイルの作成

protractorは、Karma同様に設定ファイルを指定してテストを実行する。そのためひな形をコピーして設定ファイルを作成する。

cp ./node_modules/protractor/referenceConf.js referenceConf.js

設定ファイルにはかなり丁寧なコメントが書かれている。何点かピックアップしてみると、

  • クロムだけでテストするならchromeDriverだけでseleniumSeverJarは指定しなくてもよい
  • SauceLabsがサポートされている
  • テストのための引数とかも設定可能 --params.login.user 'Joe'
  • Mochaはベータサポート :(

mocha, chaiを使うための設定

ベータ版のようだが、基本的にはmocha・chaiが使える。

mocha, chai, chai-as-promisedのインストール

mochaはグローバルでのインストールが必要っぽい。あと、protractorはpromiseが各所で使われているのでchai-as-promisedが推奨されている。

npm install -g mocha
npm install chai
npm install chai-as-promised

設定ファイルの変更

設定ファイルのframeworkをmochaにする。デフォルトはjasmine。確かKarma自体にE2Eが合った時はオレオレ仕様だったので一般的なフレームワークが使えるのはかなりありがたい。

framework: 'mocha',

mochaのオプションを設定

mochaOpts: {
    ui: 'bdd',
    reporter: 'spec'
},

reporterをspecにしておいた(デフォルトはlist)

テストファイル

公式で紹介されているプラスαでこんな形になる。

/*jshint -W079 */

var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;

describe('angularjs homepage', function() {
  it('should greet the named user', function() {
    browser.get('http://www.angularjs.org');
    element(by.model('yourName')).sendKeys('Julie');
    var greeting = element(by.binding('yourName'));

    expect(greeting.getText()).to.eventually.equal('Hello Julie!');
  });
});

chai, chai-as-promisedを読み込んで設定しておき、expectをchaiのものに上書きをしておく。なお、jshintを使っている場合はexpectの上書きは警告がでるとおもうので、冒頭で無視するように/*jshint -W079 */を記述しておく。

expectはeventuallyを挟んでpromiseに対応しておく。

* * *

angular.js自体が非常に大きなライブラリで様々な物を含んでいるが、エコシステムとしてテスト環境も整備されている。好みの問題だが身を委ねてしまうと非常に楽になる。

  1. niwaringoの投稿です