話題のテストツール Steak を勉強してみた

以前 Ruby on Railsの達人 @a_matsuda に 薦められた テストツール Steak を現在開発中のプロジェクトで使ってみようと思い、勉強してみました。

Steakとは

Steak は Cucumber 同様に Ruby on RailsなどのWebアプリの受け入れテストや総合テストのツールです。Cucumberは仕様(テスト)を自然言語で記述できるのが大きな特徴でしたが、SteakではRSpec+専用DSLで記述しまます。


Cucumberは仕様(テスト)を自然言語で書けるので、ユーザーに仕様を確認してもらうとか作ってもらえるのが最大のメリットです。しかしその分、feature(仕様)とStep(仕様をプログラムをつなぐコード)の2つを記述する必要あり開発の手間が取られます。


それに比べ、 Steak は RSpec (= Rubyのコード) なので、プログラマーに取っては、読み書きは容易です。
受け入れテストや総合テストでは表示結果(html) を確認するコードが必要になりますが、 Steak では Capybala を使い CSSセレクターでhtmlの要素を指定できるので簡潔な記述が出来ます。
CSSセレクターは、デザイナーではないプログラマーでもjQuery等でも使われているのでお馴染みだと思います。

インストール

インストールは Steak ページに書かれているように bundle (Rails3)なら簡単です。

1. Gemfileに以下を追加し、 bundle install を実行

group :test, :development do
  gem "rspec-rails"
  gem 'steak'
  gem 'capybara'
  gem 'spork'
end

2. プロジェクト内で以下の初期化コマンドの実行

$ rails g rspec:install
$ rails g steak:install

後は rails generate steak:spec コマンドで feature(仕様)ファイルを作り、そこにコードを書いて行きます

テスト対象の作成

勉強なので簡単なプログラムということで scaffold で出来たコードをテストしてみる事にしました。

$ rails new todo_steak -T
$ cd todo_steak
$ rails g rspec:install
$ rails g steak:install
$ rails g scaffold todo due:date task:string
$ rake db:migrate
$ RAILS_ENV=test rake db:migrate
$ vi spec/fixtures/todos.yml            <= テストデータの作成
-----------
todo01:
  due: 2011-02-18
  task: 打ち合わせ

todo02:
  due: 2011-02-20
  task: 開発
-----------
$ rake db:migrate
$ RAILS_ENV=test rake db:migrate
$ vi app/models/todo.rb       <= validation の追加
------------
class Todo < ActiveRecord::Base
  validates_presence_of :task
end
------------
$ vi app/controllers/todos_controller.rb
------------
@todos = Todo.all
        ↓                                  <= ソート指定
@todos = Todo.order(:due).all
------------

テストコード

1. 一覧表示機能 spec/acceptance/list_feature_spec.rb
require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')

feature "Todo一覧の表示" do
  fixtures :todos

  scenario "Todo一覧が表示できる" do
    visit list_page
    
    page.should have_css("h1", :text => "Listing todos")
    page.all("table tr:eq(2) td")[0..1].map{|e| e.text}.should == ["2011-02-18", "打ち合わせ"]
    page.all("table tr:eq(3) td")[0..1].map{|e| e.text}.should == ["2011-02-20", "開発"]
  end
end

scenarioに仕様(テスト)を書きます。

  • vist list_page はでistページをアクセス
  • list_page は spec/acceptance/support/paths.rb で具体的なURLを定義しています
module NavigationHelpers
  def list_page
    "/todos"
  end

  def new_page
    "/todos/new"
  end
end
RSpec.configuration.include NavigationHelpers, :type => :acceptance
  • page.should have_css("h1", … はページ上に Listing todos と書かれたh1タグが在ることを検証します
  • 次の2行は
    • 一覧ページには、Todo情報がtableタグで表示されますので、tableの2行目、3行目にfixture で与えた値が表示されているか検証しています
    • CSSセレクター "table tr:eq(2) td"はtableタグ内の2番目のtrタグ内の全てのtdタグを選択します
    • 選択された tdダグの1番目2番目の内容(文字列)を取り出し、ご存じRSpec の should == で検証しています
    • この部分は何かcoolなhelperを定義してスマートに記述したいですね

テストの実行は RSpec なので spec コマンドや rake spec です。

2. 新規作成機能 spec/acceptance/new_feature_spec.rb
require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')

feature "Todoの新規作成" do
  fixtures :todos

  scenario "Todoが新規作成できる" do
    visit new_page

    within "form" do
      select "2011", :from => "todo_due_1i"
      select "February", :from => "todo_due_2i"
      select "10", :from => "todo_due_3i"
      fill_in "Task", :with => "テスト"
      click_button "Create Todo"
    end

    page.should have_css "#notice", :text => "Todo was successfully created."

    Todo.count.should == 3
    Todo.last.due.to_s.should == "2011-02-10"
    Todo.last.task.should == "テスト"
  end

  scenario "Taskが空ならエラーになる" do
    visit new_page

    within "form" do
      select "2011", :from => "todo_due_1i"
      select "February", :from => "todo_due_2i"
      select "10", :from => "todo_due_3i"
      fill_in "Task", :with => ""
      click_button "Create Todo"
    end

    page.should have_content "Task can't be blank"
  end
end

最初の scenario は新規登録ページを表示し、フォームに入力しsubmitボタンを押すと成功しRDBにデータが出来ている事を検証しています。

  • within "form" do でformタグ内を操作(検証)対象とします
  • select "2011", :from => "todo_due_1i" は id が todo_due_1i のselectタグで 2011 を選択します
  • fill_in "Task", :with => "テスト" は Task というラベルのinputフィールドに "テスト"と入力します。input タグの指定は id や name 対応する label などが使えます
  • click_button "Create Todo" で Create Todo ボタンが押され、フォームの値がpostされます
  • page.should have_css.... は成功メッセージが表示されている事を検証しています
  • Todo.から始まる3行はTodoテーブルにフォームから入力した値でレコードが出来ている事を検証しています

つぎの scenario は入力エラーチェックの検証です。Taskフィールドに何も入力されてない場合はエラーが表示される事を確認しています
記述は、上とほぼ同じなので省略します。

Steak の感想

  • Steak はRSpecなのでビューレベルの検証もモデルレベルの検証も同じように、同じファイルに書けるは嬉しいです
  • RSpecなので spork と組み合わせるとテストの実行は高速です、開発時にはたいへん嬉しいですね
  • CSSセレクターの :eq(n) は jQueryでは 0 から始まりますが、Capybara では 1 から始まります、合わせて欲しいですね
  • Javascriptも含めテストできるという capybara-envjs を試したのですが、テストした jQueryのコードは動きませんでした。まだ開発途上らしいです。将来に期待しましょう !!

ということで、今回のプロジェクトでは Steak を使って 総合テストを書いてみようと思います。プロジェクトが終わったら、また Steak に付いて書きたいと思います。