スタッフブログ

  • カテゴリ 技術全般 の最新配信
  • RSS

RailsアプリをCucumberでテストする

 : 技術全般 2009/6/24 12:53

Blogger's Avatar

satoです。

テストと言えばユニットテストとかはだいぶ充実してきましたが、機械的なコードになりがちなので、プログラマ以外のテストケースの検証が難しいという問題があり、別途説明用にテストケースを書き直したりする必要がありました。

Ruby on Railsの話になりますが、Cucumberというツールがあるようです。
これは、YAMLで受け入れテストを書くことによって、Railsアプリのテストを行えるようにするものです。
ちなみにこれは正確にはBDD(Behavior Driven Development/ビヘイビア駆動開発)のためのツールらしいです。

Cucumberのページを開くと、以下のようなものが記述されています。
Feature: Search courses
  In order to ensure better utilization of courses
  Potential students should be able to search for courses

  Scenario: Search by topic
    Given there are 240 courses which do not have the topic "biology"
    And there are 3 courses A,B,C that each have "biology" as one of the topics
    When I search for "biology"
    Then I should see the following courses:
      | title |
      | A     |
      | B     |
      | C     |
見た感じだとただの英文に見えます(YAMLっぽい部分が若干ありますが)が、Cucumberではこれをテストコードとして解釈して実行させることができます。

■インストール
CucumberはRails2.1以降で動作します。
gem install rspec rspec-rails cucumber webrat
でインストールできます。
また、別途
gem install term-ansicolor treetop diff-lcs nokogiri builder
あたりをインストールしておくといいようです。

■テスト用プロジェクト作成
とりあえずブログアプリケーションのようなものを作る想定で作ります。
rails blog
cd blog
script/generate scaffold entry name:string title:string body:text
rake db:migrate
http://127.0.0.1:3000/entries/とかで、表示ができていることを確認してください。

■Cucumber初期化
script/generate cucumber
で、いろいろ生成されます。
script/generate feature Entry
で、Entryに対するテストコードの雛形が生成されます。
features/manage_entries.feature
features/step_definitions/entry_steps.rb

このうち、manage_entries.featureが、実際のテストコードになります。このファイルを開くと以下のように書かれています。
Feature: Manage entries
  In order to [goal]
  [stakeholder]
  wants [behaviour]

  Scenario: Register new entry
    Given I am on the new entry page
    And I press "Create"

  Scenario: Delete entry
    Given the following entries:
      ||
      ||
      ||
      ||
      ||
    When I delete the 3rd entry
    Then I should see the following entries:
      ||
      ||
      ||
      ||
Featureは、このテストシナリオ全体の内容を記載します。(Featureの直後に書いてあるのは概要のようなものです)
実際の動作は、各Scenarioに記述していきます。

とりあえずScenarioを書いてみます。
Feature: Manage entries
  In order to [goal]
  [stakeholder]
  wants [behaviour]

  Scenario: Register new entry
    Given I am on the new entry page
    When I fill in "Name" with "user"
    And I fill in "Title" with "blog title"
    And I fill in "Body" with "blog content"
    And I press "Create"
    Then I should see "user"
    And I should see "blog title"
    And I should see "blog content"

  Scenario: Delete entry
    Given the following entries:
      | name  | title  | body  |
      | name1 | title1 | body1 |
      | name2 | title2 | body2 |
      | name3 | title3 | body3 |
      | name4 | title4 | body4 |
      | name5 | title5 | body5 |
    When I delete the 3rd entry
    Then I should see the following entries:
      | name  | title  | body  |
      | name1 | title1 | body1 |
      | name2 | title2 | body2 |
      | name4 | title4 | body4 |
      | name5 | title5 | body5 |
これで、

rake features

すると、以下のようにテストが実行されます。
/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/lib:lib" "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/bin/cucumber" --format pretty features/manage_entries.feature
Feature: Manage entries
  In order to [goal]
  [stakeholder]
  wants [behaviour]

  Scenario: Register new entry               # features/manage_entries.feature:6
    Given I am on the new entry page         # features/step_definitions/webrat_steps.rb:6
    When I fill in "Name" with "user"        # features/step_definitions/webrat_steps.rb:22
    And I fill in "Title" with "blog title"  # features/step_definitions/webrat_steps.rb:22
    And I fill in "Body" with "blog content" # features/step_definitions/webrat_steps.rb:22
    And I press "Create"                     # features/step_definitions/webrat_steps.rb:14
    Then I should see "user"                 # features/step_definitions/webrat_steps.rb:93
    And I should see "blog title"            # features/step_definitions/webrat_steps.rb:93
    And I should see "blog content"          # features/step_definitions/webrat_steps.rb:93

  Scenario: Delete entry                     # features/manage_entries.feature:16
    Given the following entries:             # features/step_definitions/entry_steps.rb:1
      | name  | title  | body  |
      | name1 | title1 | body1 |
      | name2 | title2 | body2 |
      | name3 | title3 | body3 |
      | name4 | title4 | body4 |
      | name5 | title5 | body5 |
    When I delete the 3rd entry              # features/step_definitions/entry_steps.rb:5
    Then I should see the following entries: # features/step_definitions/entry_steps.rb:12
      | name  | title  | body  |
      | name1 | title1 | body1 |
      | name2 | title2 | body2 |
      | name4 | title4 | body4 |
      | name5 | title5 | body5 |

2 scenarios (2 passed)
11 steps (11 passed)
0m1.040s

passedとなっているので、正常終了しているようです。

試しに失敗させてみます。

app/controllers/entries_controller.rb
  def destroy
    @entry = Entry.find(params[:id])
    # @entry.destroy     # ここをコメントアウト

    respond_to do |format|
      format.html { redirect_to(entries_url) }
      format.xml  { head :ok }
    end
  end

rake features

すると以下のようになると思います。

/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/lib:lib" "/usr/lib/ruby/gems/1.8/gems/cucumber-0.3.11/b
in/cucumber" --format pretty features/manage_entries.feature
Feature: Manage entries
  In order to [goal]
  [stakeholder]
  wants [behaviour]

  Scenario: Register new entry               # features/manage_entries.feature:6
    Given I am on the new entry page         # features/step_definitions/webrat_steps.rb:6
    When I fill in "Name" with "user"        # features/step_definitions/webrat_steps.rb:22
    And I fill in "Title" with "blog title"  # features/step_definitions/webrat_steps.rb:22
    And I fill in "Body" with "blog content" # features/step_definitions/webrat_steps.rb:22
    And I press "Create"                     # features/step_definitions/webrat_steps.rb:14
    Then I should see "user"                 # features/step_definitions/webrat_steps.rb:93
    And I should see "blog title"            # features/step_definitions/webrat_steps.rb:93
    And I should see "blog content"          # features/step_definitions/webrat_steps.rb:93

  Scenario: Delete entry                     # features/manage_entries.feature:16
    Given the following entries:             # features/step_definitions/entry_steps.rb:1
      | name  | title  | body  |
      | name1 | title1 | body1 |
      | name2 | title2 | body2 |
      | name3 | title3 | body3 |
      | name4 | title4 | body4 |
      | name5 | title5 | body5 |
    When I delete the 3rd entry              # features/step_definitions/entry_steps.rb:5
    Then I should see the following entries: # features/step_definitions/entry_steps.rb:12
      | name  | title  | body  |
      | name1 | title1 | body1 |
      | name2 | title2 | body2 |
      | name4 | title4 | body4 |
      | name5 | title5 | body5 |
      expected: "name4",
           got: "name3" (using ==)
      Diff:
      @@ -1,2 +1,2 @@
      -name4
      +name3
       (Spec::Expectations::ExpectationNotMetError)
      ./features/step_definitions/entry_steps.rb:16
      ./features/step_definitions/entry_steps.rb:15
      (eval):3:in `each_with_index'
      ./features/step_definitions/entry_steps.rb:14:in `each'
      ./features/step_definitions/entry_steps.rb:14:in `each_with_index'
      ./features/step_definitions/entry_steps.rb:14
      (eval):3:in `each_with_index'
      ./features/step_definitions/entry_steps.rb:13:in `each'
      ./features/step_definitions/entry_steps.rb:13:in `/^I should see the following entries:$/'
      features/manage_entries.feature:25:in `Then I should see the following entries:'

2 scenarios (1 failed, 1 passed)
11 steps (1 failed, 10 passed)
0m0.884s
rake aborted!
Command failed with status (1): [/usr/bin/ruby1.8 -I "/usr/lib/ruby/gems/1....]

(See full trace by running task with --trace)

削除をコメントアウトしたので、削除の部分が失敗しているのがわかると思います。
このような感じでテストを書くことができるようになります。

英語だと若干わかりづらいのですが、有志の手によって一部を日本語で書けるようにしたものもあるようです(今回は省略します)。また、同時に生成されたstepファイル(今回だとentry_steps.rb)で、細かい実行内容の定義を書くことができます。これらを組み合わせることにより、自然な日本語でテストを書くことができるようにもなります。

従来のテストコードに比べて視認性が格段に良くなりますので、Railsで開発をしている方は是非試してみてください。

トラックバック

スタッフブログ最新
カテゴリ一覧

〒104-0061 東京都中央区銀座1丁目3番3号 G1ビル7階
お問い合わせ TEL 03-3524-8860

Copyright(c) 2012 RYUS.All Rights Reserved.