読者です 読者をやめる 読者になる 読者になる

ちゃんとテスト書き始めた話

Ruby/Rails

テストのモチベ=怖いからやる

  • 正直に白状すると、「これまでテスト書いたことない && 会社にテストの文化がない && テスト書いてる時間ない」っていう状況で、時間を割いてでもテストを書こうっていうモチベがなかなか湧かなかった。
  • そんななか、唯一、ハッキリとしたわかりやすいモチベは「デグレが怖い」という恐怖心から解放されることだった。
  • プロジェクトが大きくなるほど、自分が書いたコードがどこまで影響するか把握できなくなってくる。だからといって、変更のたびにブラウザでポチポチ一個ずつ確認する作業はだるい。
  • テストが通ってるという事実が抜群の安心感をもたらすことがわかってきて、ちゃんとテストを書くようになったというお話です。

500返ってないか怖い

  • 一番わかりやすいテスト項目として「ユーザーにエラー画面を表示していないか」というのがまずアタマに浮かんだ。
  • モデルとか変更すると、影響範囲よくわからないし、怖い。
  • 手っ取り早く全部のアクションで500返ってないかテストする方法を考えてみた。
  • response.should be_success的なレスポンスをチェックするテストにタグをつけて、全コントローラーをまたいでレスポンスをチェックするテストだけを実行するってやり方を考えてみた。全コントローラーのテストは時間かかって、たぶん手元では試さなくなりそうだから。
# spec/spec_helper.rb

RSpec.configure do |config|
  config.treat_symbols_as_metadata_keys_with_true_values = true
end
  • RSpec 2.xだとこの設定が必要らしい。
  • it 'hogehoge', status: trueみたいなのをit 'hogehoge', :statusで書けるようになる。
# lib/tasks/spec/status.rake

require 'rspec/core/rake_task'

namespace :spec do
  namespace :controller do
    RSpec::Core::RakeTask.new(:status) do |spec|
      spec.pattern = 'spec/controllers/**/*_spec.rb'
      spec.rspec_opts = '--tag status'
    end
  end
end
  • 自分でrake spec:controllers的なテストのraketaskを定義したいときはRSpec::Core::RakeTask.new(:hoge)使えばいいっぽい。
  • 全コントローラーのテストで、statusってタグがついてるものだけ実行したいので、こんな感じ。
it 'returns successfully', :status do
  get :index
  response.should be_success
end
$ rake spec:controllers:status
  • これでコントローラーをまたいでstatusタグのついたテストだけ実行できる。
  • 全コントローラーのテストは重すぎるので、これでかなり気軽にチェックできるようになると思う。
  • とりあえずステータスコードをチェックするようなテストにstatusタグをつけておく、っていうルールを共有する必要はある。

スクリプトでDBを更新するの怖い

  • ちょっと前に顧客のデータぜんぶ消しちゃった事件があったような気がする。
  • ああいうのあるし、DBを更新する系のスクリプトはちゃんとテストしたい。
  • 仕事ではrails rでスクリプトを実行するんだけど、こういうのはどうやってテストすればいいのかわからなかったので調べたり試行錯誤した。
# config/environments/test.rb

NaotySample::Application.configure do
  $LOAD_PATH.unshift "#{Rails.root}/script"
end
  • script/以下をrequireするためにパスに追加しておく
# spec/scripts/create_naoty_spec.rb

require 'spec_helper'
require 'create_naoty'

describe CreateNaoty do
  it 'creates naoty' do
    CreateNaoty.run
    User.where(name: 'naoty').first.should be_present
  end
end
  • スクリプトを読み込んでテスト内で実行する。
  • モジュールのテストとたぶん同じやり方だと思う。やったことないけど。
# script/create_naoty.rb

module CreateNaoty
  def self.run
    User.create(name: 'naoty')
  end
end

CreateNaoty.run if __FILE__ == $0
  • 処理の中身をモジュールにまとめておいて、テスト内で実行しやすくしておく。
  • if __FILE__ == $0requireされたときにスクリプトが実行されるのを回避してる。
# Guardfile

guard 'rspec' do
  watch(%r{^script/(.+)\.rb$}) {|m| "spec/scripts/${m[1]}_spec.rb" }
end
  • Guardを使って自動テストをやってるので、スクリプトテスト用の設定を追加しておく。
  • これでスクリプトを変更したときに、それのテストを自動で実行するようになる。

まとめ

  • 「怖いからテストする」というモチベはわかりやすい。
  • 「怖いところをテストする」という方針であれば、「何をテストすべきか」を自ずと意識するようになる。
  • rspec-railsで対応できなければ、rspecの便利機能を駆使して試行錯誤する。RSpec bookにお世話になってる。

参考

The RSpec Book (Professional Ruby Series)

The RSpec Book (Professional Ruby Series)