[Ruby]Rspec::Storyの使い方
Rspecは単体テストだけでなく、機能テストも出来る。
最近「ユースケース実践ガイド―効果的なユースケースの書き方 (OOP Foundations)」を読んでいて、もしやこれバイブル?と思っているのだが、この本で言う「ユースケース」をStoryと捉えて自動テストできるようにしたフレームワークが、Rspec::Storyではないかと思う。
「ユースケース実践ガイド―効果的なユースケースの書き方 (OOP Foundations)」では、ユースケースはシナリオの集合体であると定義している。Rspec::Storyでも、Storyは複数のscenarioから成り立つ。ということは、まず主scenarioを書いて、それから代替scenarioを書いていけば良いわけだ。
ユースケース実践ガイド―効果的なユースケースの書き方 (OOP Foundations)
- 作者: アリスターコーバーン,Alistair Cockburn,ウルシステムズ株式会社,山岸耕二,矢崎博英,水谷雅宏,篠原明子
- 出版社/メーカー: 翔泳社
- 発売日: 2001/11
- メディア: 単行本
- 購入: 5人 クリック: 81回
- この商品を含むブログ (36件) を見る
で、実際のRspec::Storyの使い方は に詳しいのだが、Railsのことしか書いていないので、普通のRubyアプリで機能テストしようとするとなかなか苦労する・・というわけで、苦労した結果をまとめておく。
1.ストーリーファイルを書く
これは と同じ。
David ChelmskyのBlog(David Chelimsky » Blog Archive » Story Runner in Plain English)に、プレーンテキストで書けるようにしたよ!Wao!的な文言があって微笑ましい。
# #ファイル名:summary # Story: 生徒の成績一覧ファイルを、集計コマンド(summary.rb)を使用して集計する アクター : 高校教師 目的 : 成績一覧CSVファイルを元に、年度別の平均点、偏差値を集計する。 結果 : 集計結果CSVファイルを得る。 Scenario: 平成17年度の成績を集計する Given tempディレクトリに移動している And 平成17年度成績一覧.csv がテストディレクトリに存在する When ユーザーが 平成17年度成績一覧.csv を引数として、summary.rbコマンドを実行する Then tempディレクトリを削除している
Note:
- Blog では「Act as 役割、I want したいこと、So that こうなる」になっているが、変えても大丈夫かな?Rspecとしてどこかで文言を意識しているのかも。。
2.Stepを書く
# #ファイル名:summary_step.rb # class SummarySteps < Spec::Story::StepGroup steps do |define| define.given("tempディレクトリに移動している") do @pwd = Dir.pwd @test_dir = File.expand_path(File.dirname(__FILE__)) @temp_dir = @test_dir + '/temp' Dir::mkdir(@temp_dir) end define.given("$file_name がテストディレクトリに存在する") do |file_name| FileUtils.cp(File.join(@test_dir, file_name), @temp_dir) end define.then("tempディレクトリを削除している") do Dir::chdir(@pwd) FileUtils.remove_entry(@temp_dir) end end end
フィルタコマンド系のテストをするときは、tempディレクトリを作って素材ファイルを作って最後にtempディレクトリを削除する、という処理をしないといけないので、ここに素材として置いておく。
Note:
- requireは不要。実際に必要なライブラリをrequireするのは、呼び出し元のsummary.rbが実施するので。
- 「steps_for :ストーリー名 do; Given 〜 」という書き方ができるかどうか不明。。→少なくとも、http://blog.davidchelimsky.net/articles/2007/10/21/story-runner-in-plain-english では書き方が違う。(2007/10/21の記事なので古いだけかも)
- 各stepでは、インスタンス変数は共有される。Spec::Story::StepGroupの子(SummaryStep)のインスタンス変数なのだから当然か・・。
3.呼び出しスクリプトを書く
requireをどうするかが問題だった。。
# #ファイル名:summary.rb # require 'rubygems' require 'spec/story' require 'spec' require File.join(File.dirname(__FILE__), '../lib/summary') # テスト対象が ~/lib/summary.rb の場合 require File.join(File.dirname(__FILE__), File.basename(__FILE__, ".rb") + '_steps') # assumes the other story file is named the same as this file minus ".rb" runner = Spec::Story::Runner::PlainTextStoryRunner.new(File.expand_path(__FILE__).gsub(".rb","")) runner.steps << SummarySteps.new runner.run
4.テストする。
C:\summary\test>ruby -Ks summary.rb Running 1 scenarios Story: 生徒の成績一覧ファイルを、集計コマンド(summary.rb)を使用して集計する アクター : 高校教師 目的 : 成績一覧CSVファイルを元に、年度別の平均点、偏差値を集計する。 結果 : 集計結果CSVファイルを得る。 Scenario: 平成17年度の成績を集計する Given tempディレクトリに移動している And 平成17年度成績一覧.csv がテストディレクトリに存在する When ユーザーが 平成17年度成績一覧.csv を引数として、summary.rbコマンドを実行する (PENDING) Then tempディレクトリを削除している 1 scenarios: 0 succeeded, 0 failed, 1 pending Pending Steps: 1) 生徒の成績一覧ファイルを、集計コマンド(summary.rb)を使用して集計する (平成17年度の成績を集計する): ユーザーが 平成17年度成績一覧.csv を引数として、summary.rbコマンドを実行する C:\summary\test>