成らぬは人の為さぬなりけり

エンジニアライフをエンジョイする為のブログ

TypeScript+Jasmine+PhantomJs+GuardでTDDしてみる

Ruby2.0の勉強そっちのけで、gem作ってました。。。

というわけで、仕事で使う為ではあったんですが、
勉強も兼ねて、

  • TypeScriptとSpecの更新を監視
  • TypeScirptが更新されたら更新したファイルのSpecを実行
  • Specが更新されたら、テスト対象のTypeScriptファイルをコンパイルして、Spec実行
  • Enter押したら全テスト実行

という、まさにguard-rspecがやってくれる事を、
Jasmineでもやりたいよ!と思って作りました。

動作確認済み環境

使い方

誠に勝手ではありますが、こいつは単体では動きません。。。
先にインストールしておく必要がある物があります。

  • TypeScript (tscコマンドにPATHが通っている状態が必要)
  • PhantomJs (phantomjsコマンドにPATHが通っている状態が必要)

順を追って使い方をメモしておきます。

TypeScriptのインストール

node.jsとnpmのインストールに関しては、
HomebrewでNode.js入れて、npmでCoffeeScriptを入れる - 成らぬは人の為さぬなりけり
こちらを参照して頂ければ良いかと。
※最新のnode.jsをbrewでインストールするとnpmのモジュールにPATH通らなかったような気も、、、

というわけで、npmさえインストールできていれば、

$ npm install typescript

で良いはずです。

PhantomJsのインストール

PhantomJsのインストールに関しては、
PhantomJs+Vert.x(JRuby)でSockJsの疎通確認をしてみる - 成らぬは人の為さぬなりけり
こちらを見ていただければ分かる通り。

$ brew install phantomjs

で完了です。(Macの場合のみですが、、、)

Linuxをお使いの方は、以下を参照してください。
PhantomJS: Download and Install

Gemfileの準備

bundlerでインストールする為に、Gemfileを準備します。

source 'https://rubygems.org'

gem 'rb-fsevent'
gem 'guard'
gem 'guard-jasmine-phantomjs', :git => 'git://github.com/yagince/guard-jasmine-phantomjs.git'

「rb-fsevent」は無くても動くとおもいます。多分、、、。
リポジトリをcloneして、pathで指定してもOKです。

そして、bundlerでインストール。

$ bundle install --path vendor/bundle

Guard設定ファイルの準備

Guard用の設定ファイルはコマンドで生成できます。

$ bundle exec guard init

これで、ルートディレクトリに「Guardfile」が出来たとおもいます。

# A sample Guardfile
# More info at https://github.com/guard/guard#readme

guard :jasmine_phantomjs, {
  compile: :typescript,
  src_dir: 'src',
  spec_dir: 'spec',
  jasmine_version: '1.3.1',
  phantomjs: :gem
  # , out: 'src/all.js'
  # , root_script: 'src/root.ts'
} do
  watch(%r{^src\/(.+?)\.ts$})
  watch(%r{^spec/(.+?)\Spec.js$}){|m| "src/#{m[1]}.ts" }
end

こんな感じになっているかと思います。

それぞれの設定に関しては、READMEを参照してください。

では、実際にこれを実行しておいて、ソースを書いていきます。

$ bundle exec guard
22:30:43 - INFO - Guard uses TerminalTitle to send notifications.
22:30:43 - INFO - Start jasmine-phantomjs.
22:30:43 - INFO - Start all script compile
22:30:44 - INFO - scripts compile finished.
22:30:44 - INFO - Guard is now watching at '/xxxx/workspace/typescript-samples/jasmine-test'

この時点で、tsファイルがあれば、全部コンパイルしてくれます。

Specを書く

ではspecを書いてみます。
spec/HogeSpec.js

describe("Hoge", function() {
    describe("#name", function() {
        it("should be return hoge", function() {
            expect(new Hoge().name()).toEqual('hoge');
        });
    });
});

保存するとテスト対象のファイルがコンパイルされます。

22:35:27 - INFO - Start compile ["src/Hoge.ts"]
22:35:27 - ERROR - Compile Error!

Error reading file "src/Hoge.ts": File not found

22:35:27 - INFO - Start compile finished.

はい、まだテスト対象のファイルを作っていないので、not foundと怒られました。

テスト対象のソースを書く

では、テスト対象のソースを書いてみます。
src/Hoge.ts

class Hoge{
}

この時点で保存してみます。

22:38:21 - INFO - Start compile ["src/Hoge.ts"]
22:38:21 - INFO - Start compile finished.
22:38:21 - INFO - Start jasmine.
22:38:22 - INFO - Jasmine execute result.

finished in 0.004s
1 examples, 1 failures

Failures:

  1) Hoge #name should be return hoge.
    TypeError: 'undefined' is not a function

コンパイルは通りました。
が、まだメソッドを実装していないので、テストはfailしています。

メソッドを実装する

では、メソッドを実装してみます。

class Hoge{
    name(): string { return 'hoge'; }
}
2:40:19 - INFO - Start compile ["src/Hoge.ts"]
22:40:19 - INFO - Start compile finished.
22:40:19 - INFO - Start jasmine.
22:40:20 - INFO - Jasmine execute result.

finished in 0.004s
1 examples, 0 failures

テストも通りました。

Specが存在しないTypeScriptファイルを作ってみる

基本的には全て単体テスト書きたいですが、
どーしても書きづらかったりする時がたまーにあります。
なので、Specが存在しないtsファイルを作成、編集した場合どうなるかやってみます。

src/Foo.ts

class Foo {
    something(i: number): void {
        // do something...
        console.log(i);
    }
}
22:45:28 - INFO - Start compile ["src/Foo.ts"]
22:45:29 - INFO - Start compile finished.
22:45:29 - INFO - Start jasmine.

「Start jasmine」と表示はされましたが、これはテストを実行していません。
※startって出てしまうのは、手抜きです、、、すみません。。。
specが存在しない場合はテストを実行しないようになっています。

SpecRunner.htmlのファイルの読み込み順序

今の状態だと、tsファイルを全て個別にコンパイルする状態になっており、
SpecRunner.htmlのscriptタグの順序も保証されません。
なので、読み込み順序をかっちりしたい場合は、

  • out
  • root_script

というオプションを使用してください。
まずは、

  • out

このオプションを指定すると、「--out」オプションを付けてコンパイルします。
つまり、一個に纏めます。

  • root_script

このオプションを指定すると、常に指定されたファイルのみをコンパイルします。
outオプションと併用する事で、
root_scriptオプションで指定したscriptから参照(reference path="")を辿って
tscが必要な物を全部コンパイルして、
一つのファイルにまとめてくれます。


今回のgemの使い方は以上です。

明日から、またRuby2.0やります。多分w