Rails3 で RSpec + RCov 日本語対応

Rails3 でプロジェクトを作成し、RSpecRCov を日本語で動作させるまでの手順です。
rails (3.0.0) や rspec (2.0.0.beta.22) 等の必要な gem は既にインストールされているものとします。

まずは、普通に Rails プロジェクトを作成して、config/database.yml を開発環境に合わせて設定しておきます。

$ rails new project
      create
      create  README
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
     :
     :

プロジェクト内にて RSpecRCov を使用するために Gemfile に下記を追記します。

gem "rcov"
group :test do
  gem 'rspec-rails', '>= 2.0.0.beta', :group => :development
end

プロジェクトのテストフレームワークRSpec にするために
config/application.rb に下記のような修正を加えます。

module Project
  class Application < Rails::Application
    config.generators do |g|
      g.test_framework :rspec
    end
  end
end

RSpec テスト用の設定ファイルを設置します。

$ ./script/rails generate rspec:install
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  autotest
      create  autotest/discover.rb

ここまで基本的な準備は完了です。
controller などを作成すると、同時に rspec 用のテストファイルが生成されます。

$ ./script/rails generate controller user index
      create  app/controllers/user_controller.rb
       route  get "user/index"
      invoke  erb
      create    app/views/user
      create    app/views/user/index.html.erb
      invoke  rspec
      invoke  helper
      create    app/helpers/user_helper.rb
      invoke    rspec
      create      spec/helpers/user_helper_spec.rb

これで RSpec を実行する環境はできましたので、下記のようにテストを書いてみます。
1 行目の "# coding: utf-8" を記述しておくことで、テスト内容を日本語 UTF-8 で記述して実行することが可能になります。

# coding: utf-8

require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe UserController do
  describe 'index method' do
    it 'GET "index" リクエストが成功すること' do
      get 'index'
      response.should be_success
    end
  end
end

次に RSpec で記述したテストのカバレッジを確認するために RCov を実行したいのですが、
普通に rake spec:rcov を実行すると、下記のようなエラーが出て実行できません。
これは、RSpec テスト内の日本語 UTF-8 が問題で文字を正常に認識できていないために発生する問題のようです。

$ rake spec:rcov
(in /Users/nedate/svn/project)
/usr/local/bin/ruby -S bundle exec rcov --exclude /gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec- "./spec/controller/user_spec.rb"
** WARNING: Ruby 1.9 Support is experimental at best. Don't expect correct results! **
/usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/file_statistics.rb:115:in `block in is_code?': invalid byte sequence in US-ASCII (ArgumentError)
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/file_statistics.rb:112:in `each'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/file_statistics.rb:112:in `each_with_index'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/file_statistics.rb:112:in `is_code?'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/file_statistics.rb:234:in `extend_heredocs'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/file_statistics.rb:40:in `initialize'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/formatters/base_formatter.rb:50:in `new'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/formatters/base_formatter.rb:50:in `add_file'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/code_coverage_analyzer.rb:143:in `block (2 levels) in dump_coverage_info'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/code_coverage_analyzer.rb:142:in `each'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/code_coverage_analyzer.rb:142:in `block in dump_coverage_info'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/code_coverage_analyzer.rb:135:in `each'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/lib/rcov/code_coverage_analyzer.rb:135:in `dump_coverage_info'
        from /usr/local/lib/ruby/gems/1.9.1/gems/rcov-0.9.9/bin/rcov:433:in `block in <top (required)>'
rake aborted!
Command failed with status (1): [/usr/local/bin/ruby -S bundle exec rcov --...]

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

これは、spec/spec_helper.rb に下記のように記述することで回避できます。

#
# quick monkey patch for rcov
#
# http://codefluency.com/post/1023734493/a-bandaid-for-rcov-on-ruby-1-9
#
if defined?(Rcov)
  class Rcov::CodeCoverageAnalyzer
    def update_script_lines__
      if '1.9'.respond_to?(:force_encoding)
        SCRIPT_LINES__.each do |k,v|
          v.each { |src| src.force_encoding('utf-8') }
        end
      end
      @script_lines__ = @script_lines__.merge(SCRIPT_LINES__)
    end
  end
end