messaliberty

hulor と開発チームの最新情報

[Ruby] RSpec の使い方 – 01 クイックスタート

RSpec は Ruby の BDD(Behavior Driven Development). テストフレームワークの1つです。

Install RSpec

% sudo gem install rspec

Quickstart

下の方にあるサンプルコードのディレクトリ構成です。
lib/ ディレクトリに開発するライブラリを配置。
spec/ ディレクトリに spec(テスト) コードを配置。

  • sample_project/
    • lib/sample.rb サンプルのライブラリ
    • spec/
      • sample_spec.rb sample.rb のテストプログラム
      • spec_helper.rb すべてのテストで共通する処理を書くスクリプト

ディレクトリを作成


まず、プロジェクトのディレクトリを作成して、その中に lib/ と spec/ ディレクトリを作成します。

% mkdir sample_project
% cd sample_project
% mkdir lib
% mkdir spec

spec_helper.rb を作成

sample_project/spec/spec_helper.rb :

# encoding: utf-8

require "rubygems"
require 'test/unit'
require "spec"
$LOAD_PATH << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))

__END__

Needed "require 'test/unit'" before "require 'spec'" due to an error below with autospec.

/home/ice/.gem/ruby/1.8/gems/rspec-1.1.11/lib/spec.rb:25:in `exit?': undefined method `run?' for Test::Unit:Module (NoMethodError)
        from /home/ice/.gem/ruby/1.8/gems/rspec-1.1.11/lib/spec/runner.rb:192:in `register_at_exit_hook'
        from spec/messa/db/clawler/page_data_spec.rb:9

ref: http://www.ruby-forum.com/topic/170889

spec(テスト) プログラムの作成

開発するライブラリの方ではなく、テストプログラムの方を先に作成。

このテストは lib/sample.rb 内の Sample クラスの mix() メソッドをテストするプログラムです(まだ作ってないけど)。
そして作成したら↓の様にテストを実行します :

% cd sample_project
% spec -c spec/sample_spec.rb

sample_project/spec/sample_spec.rb :

# -*- coding: utf-8 -*-

require File.expand_path(File.join('.', 'spec_helper'), File.dirname(__FILE__))
require 'sample' # for lib/sample.rb

describe Sample, "When mix two numbers," do
  before(:each) do
    @sample = Sample.new
  end

  it "should calculate as +" do
    @sample.mix(1, 1).should == 2
    @sample.mix(100, 11).should == 111
    @sample.mix(10, -109).should == -99
  end
end

RSpec は Ruby の Ojbect を拡張しているので、普段使ってるクラスに should などの見慣れないテスト用メソッドが追加されてます。
should メソッドはオブジェクト(変数)をチェックする用のメソッドで下のような漢字の使い方をします。

100.should == 100
1000.should >= 1
true.should be_true
false.should_not be_true

should() の最初のパラメータ (== とか >= とか be_true) はマッチャー(matcher) と呼ばれているらしい。
詳しいことは次回以降のエントリーで。

結果 :

% spec -c spec/sample_spec.rb
/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- sample (LoadError)
        from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
        from ./spec/sample_spec.rb:4
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:14:in `load'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:14:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:13:in `each'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:13:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/options.rb:98:in `run_examples'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/command_line.rb:10:in `run'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/bin/spec:4
        from /usr/bin/spec:19:in `load'
        from /usr/bin/spec:19

spec を実行するとエラーが発生するはずです。
テスト対象のライブラリ lib/sample.rb をまだ作成してないので当然ですが・・・。
でもテストファーストなので失敗してもOKです。

テスト対象のライブラリを作成

先ほど発生したエラーは 「sample なんてファイルねえよ」 とか。(“no such file to load — sample (LoadError)“.)
なので sample_project/lib/sample.rb をとりあえず作成します。中身はカラのままでOK。
そして spec を実行して、失敗させます。

% spec -c spec/sample_spec.rb
./spec/sample_spec.rb:6: uninitialized constant Sample (NameError)
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:14:in `load'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:14:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:13:in `each'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/example_group_runner.rb:13:in `load_files'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/options.rb:98:in `run_examples'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/lib/spec/runner/command_line.rb:10:in `run'
        from /usr/lib/ruby/gems/1.8/gems/rspec-1.1.11/bin/spec:4
        from /usr/bin/spec:19:in `load'
        from /usr/bin/spec:19

またまたエラーが発生し、今度は 「Sample なんて定数ないよ」とか。 (“uninitialized constant Sample (NameError)“)
ファイルの中身を以下のように記述 :
sample_project/lib/sample.rb :

class Sample
end

spec を実行

% spec -c spec/sample_spec.rb
F

1)
NoMethodError in 'Sample When mix two numbers, should calculate as +'
undefined method `mix' for #<Sample:0xb7adbbac>
./spec/sample_spec.rb:12:

Finished in 0.061528 seconds

1 example, 1 failure

今度は 「メソッド mix() 未定義だ」とか。 (“undefined method `mix’ for #<Sample:0xb7adbbac>“)
なので、メソッド mix() を定義します :

sample_project/lib/sample.rb

class Sample
  def mix(item1, item2)
  end
end

実行結果 :

% spec -c spec/sample_spec.rb
F

1)
'Sample When mix two numbers, should calculate as +' FAILED
expected: 2,
     got: nil (using ==)
./spec/sample_spec.rb:12:

Finished in 0.026445 seconds

1 example, 1 failure

次はエラーではなく mix() の戻り値が期待したものと違った様子。(“expected: 2, got: nil (using ==)“)
メソッド mix() を以下のように実装します :

sample_project/lib/sample.rb

class Sample
  def mix(item1, item2)
    return item1 + item2
  end
end

spec を実行

% spec -c spec/sample_spec.rb
.

Finished in 0.030934 seconds

1 example, 0 failures

spec がパスしました。

こんな感じでテストを書いて、実装、テストというサイクルを繰り返して開発を進めます。

そのうち RSpec についてもう少し説明しようと思います。

最終的なサンプルコード

sample_project/spec/spec_helper.rb

# encoding: utf-8

require "rubygems"
require 'test/unit'
require "spec"
$LOAD_PATH << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))

sample_project/spec/sample_spec.rb

# -*- coding: utf-8 -*-

require File.expand_path(File.join('.', 'spec_helper'), File.dirname(__FILE__))
require 'sample' # for lib/sample.rb

describe Sample, "When mix two numbers," do
  before(:each) do
    @sample = Sample.new
  end

  it "should calculate as +" do
    @sample.mix(1, 1).should == 2
    @sample.mix(100, 11).should == 111
    @sample.mix(10, -109).should == -99
  end
end

sample_project/lib/sample.rb

class Sample
  def mix(item1, item2)
    return item1 + item2
  end
end
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • email
  • MySpace
  • Reddit
  • Tumblr
  • Yahoo! Buzz
  • StumbleUpon
  • Technorati
  • Twitter

Related posts:

  1. [JRuby on Rails on GAE/J] rubygems を jar ファイルの中にまとめる GAE/J で Rails アプリを開発するとき、GAE/J 上にアップロードできるファイル数の制限の関係から、 なんとかしてファイル数を減らしたくなります。 そこで、有効なのが、使用する rubygems を jar...
  2. [Rails] ActiveRecord で DateTime を使う ActiveRecord の :datetime は ruby Time class に対応してるんだけど、 ruby の...

Related posts brought to you by Yet Another Related Posts Plugin.

Leave a Reply