エンジニアをリングする

プログラをミングしたり。

my web site twitter

Prottのテストを支える3つの施策(power-assert導入 & Protected branches & CI高速化)

f:id:yoshiko_pg:20151201005303p:plain

この記事はGoodpatchのエンジニアがお送りするGoodpatch Advent Calendar 2015の1日目の記事です!

1日目は最近Prottチームでおこなったテスト推進施策について書いてみようと思います!

私はProttというプロトタイピングツールの開発を担当しているのですが、Prottには今までサーバーサイドのコードにしか自動テストがありませんでした。
変化のサイクルが速く長期的な運用になる自社サービスは常にコードの形を変えていく必要がありますが、自動テストがないと気軽なリファクタリングをしていくことが難しくなってしまいます。
今回はテスト推進施策ということで、フロントエンド側のテスト環境構築とテストに関連する取り組みを行ったので、その内容をまとめたいと思います。

ポイントは以下の3点です!

  • フロントエンドのテスト環境を作る
    • → Karma + mocha + power-assert + babel でのテスト環境構築
  • masterにマージされるコードは必ずテストが通っていることを保証する
    • → Protected branchesの導入
  • テストの実行コストを減らす
    • → キャッシュの設定でCIを高速化

フロントエンドテスト環境構築

構成

ProttのコードはCoffeeScriptで書かれていますが、テストコードはES2015で書こう!ということになりました!
テストコードからでも新しい記法に少しずつ慣れていけるのは嬉しいですね。

テスト環境のセットアップ

$ npm install --save-dev karma karma-browserify babelify power-assert babel-plugin-espower babel-preset-es2015 karma-coffee-preprocessor coffee-script

# Karma設定ファイル生成(今回はCoffeeScriptを使用)
$ ./node_modules/.bin/karma init karma.conf.coffee
# 対話式でconfigファイルを生成できるので、テストフレームワークでmocha, ブラウザでPhantomJSを選択
# → 該当パッケージ(karma-mocha karma-phantomjs-launcher)がインストールされる

生成されたkarma.conf.coffeeを開いて少し編集します。

module.exports = (config) ->
  config.set

    basePath: ''

    frameworks: ['mocha', 'browserify'] # browserify追加

    files: [
      # 追加
      'app/bower_components/angular/angular.js' # テスト対象コードに必要なライブラリ類
      'app/bower_components/angular-mocks/angular-mocks.js'
      # ...省略...
      'app/scripts/**/*.coffee' # テスト対象コード
      'test/spec/**/*.js'    # テストコード
    ]

    exclude: []

    preprocessors:
      # 追加
      'app/scripts/**/*.coffee': 'coffee'
      'test/spec/**/*.js': 'browserify'

    # 追加
    browserify:
      debug: true
      transform: [
        ["babelify", plugins: ["babel-plugin-espower"]]
      ]

    reporters: ['progress']
    port: 9876
    colors: true
    logLevel: config.LOG_INFO
    autoWatch: true
    browsers: ['PhantomJS']
    singleRun: false
    concurrency: Infinity

これでkarmaの設定ができました。

続いてbabelです。ES2015を今すぐ使えるJSに変換してくれるbabelは、バージョン6から変換機能がプラグイン形式になりました。
ES2015を変換したい場合は、babel-preset-es2015 をインストールした上でプロジェクトルートの.babelrcに使用するプラグインを記述する必要があります。

{
  "presets": ["es2015"]
}

最後に test/spec/ 以下にES2015で簡単なサンプルテストを作成します。

import assert from "power-assert";

describe('sample test', () => {
  it('1 + 1', () => {
    assert(1 + 1 === 3);
  });
});

これで ./node_modules/.bin/karma start (グローバルにkarma-cliを入れている場合はkarma start)でテストが実行されます!

f:id:yoshiko_pg:20151201021242p:plain

見やすい表示でエラーが表示されました。

ちなみに、私が開発に使用しているRubyMineではバージョン7だとES2015のシンタックスがエラー表示になってしまいました。
8にバージョンアップの上、PreferencesLanguages & FrameworksJavaScriptJavaScript language versionECMAScript 6 にするとエラーが表示されなくなります!

Protected branchesの導入

2015年9月4日からGitHubに「Protected branches」という機能が追加され、任意のブランチを保護することができるようになりました。
Protected branchに指定されたブランチには以下の操作が禁止されます。

  • ブランチの削除
  • force push(コミット履歴を上書きできる強制プッシュ。push -f
  • テストが通る前のコードのマージ(オプション)
    • テストステータスを確認できないため、ローカルからの直接プッシュも禁止される → 大事なブランチへのうっかりプッシュを防げて嬉しい!

設定方法

設定したいリポジトリのSettingsタブ f:id:yoshiko_pg:20151201005849p:plain

Branchesを選択 f:id:yoshiko_pg:20151201005901p:plain

Protected branchesから対象にしたいブランチを選択 f:id:yoshiko_pg:20151201005915p:plain

テスト周りのオプションを選んで確定 f:id:yoshiko_pg:20151201005937p:plain

注意点

たとえばmasterに対してPull Request AとBがあった場合、今まではそのまま両方をぽちぽちとmasterにマージすることができましたが、masterがProtected branchである場合にはマージ後のコードがテストに通過することを保証しなければならないため、Aをmasterへマージしたあとに逆に一度Bに対してmaster(Aの差分)をマージしてBのテストを通過させる必要があります。
少しテスト待ちの時間がかかってしまいますが、AとBそれぞれ単体ではテストに通っていたのに両方をmasterにマージしたらmasterでテストが落ちた、というような事態を防ぐことができます。

キャッシュの設定でCIを高速化

.travis.ymlに以下の指定を追加します。

sudo: false
cache:
  directories:
    - node_modules
    - vendor/bundle

cacheのdirectoriesにnpm installとbundle install先のディレクトリを指定することでキャッシュが効き、インストール時間を大幅に短縮できます。
(bundle installのディレクトリ指定は、bundler_argsの値もしくはbefore_script内のbundle install--path vendor/bundle を追加してください)
加えて、sudo: falseを指定することでコンテナベースの環境を使用することができ、テストの開始が速くなります。
これで1回に7~9分ぐらいかかっていた実行時間が2分40秒程度に短縮されました。

参考:いまどきの.travis.yml - teppeis blog


以上3つ、最近Prottチームに取り入れたテスト推進施策をご紹介しました。

フロントエンドのテストは実行環境を構築したばかりなのでまだ数がありませんが、ES2015で書けるということもテストを書くモチベーションに加担してくれています。
コストとのバランスをとりながら、積極的にテストコードを充実させていきたいと思います!

2日目はGoodpatchが誇るサーバーサイドエンジニア、itoさんの今日からはじめる命名戦略です!