CucumberとWebratをRailsで使って、さらにautotestで動かしてGrowlで通知する

今流行りつつある話題のCucumberとWebratをRailsで使ってみました。
CucumberとWebratなんぞやという人はid:moroさんの「 Cucumberがアツい」が参考になります。


今回やったこと

  • RailsにCucumberを入れてみた
  • Featureの予約語を日本語にして動かしてみた
  • Cucumberをautotestでも動くようにしてみた
  • autotestの結果をGrowlに通知するようにしてみた


参考サイト
上記のCucumberがアツい
Cucumberのgithub

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

以下のgemが必要みたいなので、まだインストールしていない場合はインストールします。

  • rspec
  • rspec-rails
  • cucumber
  • webrat
  • term-ansicolor
  • treetop
  • diff-lcs
  • nokogiri
  • ZenTest(autotestを使うなら)
$sudo gem install rspec rspec-rails cucumber webrat term-ansicolor treetop diff-lcs nokogiri

Railsプロジェクトの作成

適当なところにプロジェクトを作成します。

$rails cucumbertest
$cd cucumbertest 

CucumberをRailsで使えるようにする

script/generateを使ってcucumberを動かすのに必要なファイルを生成します。
なお、今回はRSpecも併用するつもりなので一緒に生成しちゃいます。

$ruby script/generate cucumber
$ruby script/generate rspec

scaffoldを使ってappと一緒にspecとteatureを自動生成

今回は一から作らずに自動生成で手抜きをします。

$ruby script/generate rspec_scaffold Group name:string about:text
$ruby script/generate feature Group name:string about:text
$rake db:migrate

生成したfeatureを動かしてみる

cucumberコマンドかrakeで動かせるみたいなのでcucumberコマンドでfeatureを動かしてみる

$cucumber features

Feature: Manage groups  # features/manage_groups.feature
  In order to [goal]
  [stakeholder]
  wants [behaviour]
  Scenario: Register new group            # features/manage_groups.feature:6
    Given I am on the new group page      # features/step_definitions/group_steps.rb:1
    When I fill in "Name" with "name 1"   # features/step_definitions/webrat_steps.rb:18
    And I fill in "About" with "about 1"  # features/step_definitions/webrat_steps.rb:18
    And I press "Create"                  # features/step_definitions/webrat_steps.rb:10
    Then I should see "name 1"            # features/step_definitions/webrat_steps.rb:89
    And I should see "about 1"            # features/step_definitions/webrat_steps.rb:89

  Scenario: Delete group                     # features/manage_groups.feature:14
    Given the following groups:              # features/step_definitions/group_steps.rb:5
    When I delete the 3rd group              # features/step_definitions/group_steps.rb:9
    Then I should see the following groups:  # features/step_definitions/group_steps.rb:16


2 scenarios
9 steps passed

うごいた!!

featureの予約語を日本語にして動かしてみる

角谷さんのコミットのおかげでfeatureの予約後を以下のように日本語で書けるしい。

  • Feature → フィーチャ
  • Scenario → シナリオ
  • Given → 前提
  • When → もし
  • And → かつ
  • Then → ならば

素敵なので早速やってみた。


以下が自動生成したfeature

Feature: Manage groups
  In order to [goal]
  [stakeholder]
  wants [behaviour]
  
  Scenario: Register new groups
    Given I am on the new groups page
    When I fill in "Name" with "name 1"
    And I fill in "About" with "about 1"
    And I press "Create"
    Then I should see "name 1"
    And I should see "about 1"

  Scenario: Delete groups
    Given the following groups:
      |name|about|
      |name 1|about 1|
      |name 2|about 2|
      |name 3|about 3|
      |name 4|about 4|
    When I delete the 3rd groups
    Then I should see the following groups:
      |name|about|
      |name 1|about 1|
      |name 2|about 2|
      |name 4|about 4|

で、以下が予約後を日本語に直したもの

フィーチャ: Manage groups
  In order to [goal]
  [stakeholder]
  wants [behaviour]
  
  シナリオ: Register new group
    前提 I am on the new group page
    もし I fill in "Name" with "name 1"
    かつ I fill in "About" with "about 1"
    かつ I press "Create"
    ならば I should see "name 1"
    かつ I should see "about 1"

  シナリオ: Delete group
    前提 the following groups:
      |name|about|
      |name 1|about 1|
      |name 2|about 2|
      |name 3|about 3|
      |name 4|about 4|
    もし I delete the 3rd group
    ならば I should see the following groups:
      |name|about|
      |name 1|about 1|
      |name 2|about 2|
      |name 4|about 4|

で早速動かしてみたけど以下のようにシナリオの数が0になっている。

$cucumber features
0 scenarios

日本語で動かす指定をしなきゃいけないのかな?と思ってcucumber -hでヘルプをみてみると-lでLANGを指定できるみたい。

$cucumber -h
Usage: cucumber [options] [[FILE[:LINE[:LINE]*]] | [FILES|DIRS]]

Examples:
cucumber examples/i18n/en/features
cucumber --language it examples/i18n/it/features/somma.feature:6:98:113

    -r, --require LIBRARY|DIR        Require files before executing the features.
                                     If this option is not specified, all *.rb files that
                                     are siblings or below the features will be autorequired
                                     This option can be specified multiple times.
    -s, --scenario SCENARIO          Only execute the scenario with the given name.
                                     If this option is given more than once, run all
                                     the specified scenarios.
    -l, --language LANG              Specify language for features (Default: en)
                                     Available languages: ar, cy, da, de, en, en-lol, en-tx, es, et, fr, id, it, ja, ko, lt, nl, no, pl, pt, ro, ro2, ru, se, zh-CN
                                     Look at /opt/local/lib/ruby/gems/1.8/gems/cucumber-0.1.16/lib/cucumber/languages.yml for keywords
    -f, --format FORMAT              How to format features (Default: pretty)
                                     Available formats: pretty, profile, progress, html, autotest
                                     You can also provide your own formatter classes as long as they have been
                                     previously required using --require or if they are in the folder
                                     structure such that cucumber will require them automatically.
                                     This option can be specified multiple times.
    -o, --out FILE                   Write output to a file instead of @out_stream.
                                     This option can be specified multiple times, and applies to the previously
                                     specified --format.
    -c, --[no-]color                 Use ANSI color in the output, if formatters use it.  If
                                     these options are given multiple times, the last one is
                                     used.  If neither --color or --no-color is given cucumber
                                     decides based on your platform and the output destination
    -e, --exclude PATTERN            Don't run features matching a pattern
    -p, --profile PROFILE            Pull commandline arguments from cucumber.yml.
    -d, --dry-run                    Invokes formatters without executing the steps.
    -n, --no-source                  Don't show the file and line of the step definition with the steps.
    -i, --no-snippets                Don't show the snippets for pending steps
    -q, --quiet                      Don't show any development aid information
    -b, --backtrace                  Show full backtrace for all errors
    -v, --verbose                    Show the files and features loaded
        --version                    Show version
        --help                       You're looking at it

とりあえずcucumber features -l jaをやってみると動いた!!

$cucumber features -l ja
フィーチャ: Manage groups  # features/manage_groups.feature
  In order to [goal]
  [stakeholder]
  wants [behaviour]
  シナリオ: Register new group               # features/manage_groups.feature:6
    前提 I am on the new group page        # features/step_definitions/group_steps.rb:1
    もし I fill in "Name" with "name 1"    # features/step_definitions/webrat_steps.rb:18
    かつ I fill in "About" with "about 1"  # features/step_definitions/webrat_steps.rb:18
    かつ I press "Create"                  # features/step_definitions/webrat_steps.rb:10
    ならば I should see "name 1"            # features/step_definitions/webrat_steps.rb:89
    かつ I should see "about 1"            # features/step_definitions/webrat_steps.rb:89

  シナリオ: Delete group                        # features/manage_groups.feature:14
    前提 the following groups:                # features/step_definitions/group_steps.rb:5
    もし I delete the 3rd group               # features/step_definitions/group_steps.rb:9
    ならば I should see the following groups:  # features/step_definitions/group_steps.rb:16


2 scenarios
9 steps passed

autotestでfeatureを動かす

githubのwikiによるとautotestと組み合わせて動かせるみたいなので次はその設定をやってみる。

と思ったら以下のコマンド一発で動くと書いてある。

$ AUTOFEATURE=true autospec

.bash_profileとかに

export AUTOFEATURE=true

とか書いておくとautospecコマンドだけでfeatureも動くみたい。
私はMacだけどもしWindowsなら環境変数にセットかな?(たぶん)


だだしここでも英語のままだと動くけど、日本語のfeatureは動かなかったのでどっかで教えてあげないといけないみたいです。


あと英語のfeatureをautotestで動かしてみて思ったけど、all-goodだとテスト数とかでないみたいっすね。
この後、Growlでテストの結果を通知したいんだけどこのままじゃあんまりうれしくないんで毎回いろんな情報を出すように変更したいな〜と思いながら四苦八苦。。。


ソースとか見てみると素っ気ない情報しかでないときはautotest-allを通っていて出力フォーマットがprogressになっているからだと判明。


githubのwikiとソースを見るとcucumber.ymlを作ってcucumberコマンドに渡す引数を変更できるっぽいことが書いてる。


という訳でcucumber.ymlを作成

touch ${RAILS_ROOT}/cucumber.yml

で以下のように書く

autotest: features --f pretty --l ja
autotest-all: features --f pretty --l ja

これで日本語のfeatureが使えるようになって、かつ出力がdefaultと同じようになった。

Growlでautotestの結果を通知する

これまでの設定で出力もいい感じになったので、autotestの結果をGrowlで通知するように設定
~/.autotestに以下のように記述

require "autotest/redgreen"
module Autotest::Growl

  IMG_OK = '/Developer/Examples/Carbon/Dial/English.lproj/rotate_knob_select.tiff' #適当に使いたい画像のパスに変更する
  IMG_NG = "/Applications/Mail.app/Contents/Resources/Caution.tiff" #適当に使いたい画像のパスに変更する

  def self.growl title, msg, img="~/.rails_ok.png", pri=0
    title += " in #{Dir.pwd}"
    msg += " at #{Time.now}"
    system "growlnotify -n autotest -H localhost --image #{img} -p #{pri} -m #{msg.inspect} #{title}"
  end

  Autotest.add_hook :initialize do |at|
    # Ignore files in tmp/
    at.add_exception %r%^\./tmp%
  end

#for RSpec

  Autotest.add_hook :ran_command do |at|
    result = [at.results].flatten.join("\n")
    @result_examples = result.slice(/(\d+)\s+examples?/).to_i
    @result_tests = result.slice(/(\d+)\s+tests?/).to_i
    @result_failures = result.slice(/(\d+)\s+failures?/).to_i
    @result_errors = result.slice(/(\d+)\s+errors?/).to_i
  end

#for Cucumber
  Autotest.add_hook :ran_features do |at|
    result = [at.results].flatten.join("\n")
    @result_scenario = result.slice(/(\d+)\s+scenario?/).to_i
    @result_passed = result.slice(/(\d+)\s+(step|steps)\spassed?/).to_i
    @result_skip = result.slice(/(\d+)\s+(step|steps)\sskipped?/).to_i
    @result_pending = result.slice(/(\d+)\s+(step|steps)\spending?/).to_i
    @result_failed = result.slice(/(\d+)\s+(step|steps)\sfailed?/).to_i

  end

  Autotest.add_hook :waiting do
    if @result_failures + @result_errors + @result_skip + @result_pending + @result_failed> 0
      growl "Tests Failed",
      "RSpec #{@result_failures} failures  #{@result_errors} errors       Cucumber #{@result_failed} failed  #{@result_pending} pending #{@result_skip} skip       ", IMG_NG, 2
    else
      growl "Tests Passed", "#{@result_tests + @result_examples + @result_passed} tests", IMG_OK
    end

  end
end

毎回RSpecとCucumberのテストを計2回通知するとうるさかったのでRSpecとCucumberのテスト結果をインスタンス変数に持たせてすべてのテストが終わってwaitingになったところで通知するようにしてみました。

これで快適なテスト環境が出来た気がするので明日にでもid:moroさんの「Webratがスゴい(続:Cucumberがアツい)」を参考にfeatureの全日本語化をためしてみます。