RSpec の設定 - 不定期刊 Rails App を作る(6)

RailsApp Rails RSpec 2018年 11月 12日

テストはこれまで通り,RSpec を利用する. RSpec は Ruby における BDD(Behavior Driven Development) 手法を実現するシステムである. ここでは,RSpec の設定について記載する.

RSpec のインストール

  1. Gemfile の development, test の項目に rspec-rails を追加する
  2. group :development, :test do
      gem 'rspec-rails'
      (中略)
    end
  3. bundle する(2行目以降は結果: 変更分のみ)
  4. $ bundle
    Fetching diff-lcs 1.3
    Fetching rspec-support 3.8.0
    Installing diff-lcs 1.3
    Installing rspec-support 3.8.0
    Fetching rspec-core 3.8.0
    Fetching rspec-expectations 3.8.2
    Fetching rspec-mocks 3.8.0
    Installing rspec-expectations 3.8.2
    Installing rspec-mocks 3.8.0
    Installing rspec-core 3.8.0
    Fetching rspec-rails 3.8.1
    Installing rspec-rails 3.8.1
  5. RSpec をインストールする
  6. $ bin/rails g rspec:install
    Running via Spring preloader in process 1512
          create  .rspec
          create  spec
          create  spec/spec_helper.rb
          create  spec/rails_helper.rb
  7. 試しにテストを実行してみる.Spec を書いてないので,成功する(2行目以降は結果)
  8. $ bin/rails spec
    /Users/hkob/.rbenv/versions/2.5.3/bin/ruby -I/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/lib:/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-support-3.8.0/lib /Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb
    No examples found.
    
    
    Finished in 0.00042 seconds (files took 0.12874 seconds to load)
    0 examples, 0 failures

Spec の書き方

RSpec の使い方をわかってもらうために,和暦表示のヘルパメソッドを BDD で作ってみる(今回の仕様では和暦表示は必要ではないが).

  1. ヘルパメソッドは app/helpers/application_helper.rb に書くことにする.そのため,spec/helpers/application_helper_spec.rb を generator で作成する.
  2. $ bin/rails g rspec:helper application
    Running via Spring preloader in process 2396
          create  spec/helpers/application_helper_spec.rb
  3. 和暦関係の面倒なテストを書いてみた.context はテストの文脈を分けるために使う.subject は通常ではテストターゲットを記載することが多いのだが,今回はテストに必要なパラメータとその答えが入った配列を用意した.各仕様テストのたびに設定される.subject が呼ばれた時に遅延作成され,その後はそれが保持される.it が実際のテストで,その後のメッセージが仕様となる.it の中身で expect によるテストが実行される.今回は記載していないが,it のたびに before で用意された下準備が実行される.オブジェクトの作成・削除が無駄なので,今回は一つの it の中に複数のメソッドテストを記載してしまった.あまりオブジェクトの準備などが重くない場合には,it の中身を expect 一つだけにし,it の後ろのメッセージを省略することも可能である.この場合には,適当なメッセージが仕様として記載される.
  4. require 'rails_helper'
    
    RSpec.describe ApplicationHelper, type: :helper do
      context 'generate date strings' do
        subject { [
          [[1988, 12, 31], '昭和63', 'S63', 63, '12月', '31日'],
          [[1989,  1,  7], '昭和64', 'S64', 64,  '1月',  '7日'],
          [[1989,  1,  8], '平成元', 'H1',   1,  '1月',  '8日'],
          [[1989, 12, 31], '平成元', 'H1',   1, '12月', '31日'],
          [[2018,  4,  1], '平成30', 'H30', 30,  '4月',  '1日'],
        ] }
    
        # y, m, d がパラメータなメソッド群
        it 'wareki, ewareki wareki_num, wareki_ymd and md should have correct values' do
          subject.each do |ymd, jy, ey, y, jm, jd|
            expect(wareki(*ymd)).to eq jy
            expect(ewareki(*ymd)).to eq ey
            expect(wareki_num(*ymd)).to eq y
            jymd = "#{jy}年#{jm}#{jd}"
            expect(wareki_ymd(*ymd)).to eq jymd
            expect(md(ymd[1], ymd[2])).to eq "#{jm}#{jd}"
          end
        end
      end
    end
  5. テストを実行してみると以下のようになる(2行目以降は結果).分かりにくいが 3 行目の F が失敗を意味している.
  6. $ bin/rails spec
    /Users/hkob/.rbenv/versions/2.5.3/bin/ruby -I/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/lib:/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-support-3.8.0/lib /Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb
    F
    
    Failures:
    
      1) ApplicationHelper generate date strings wareki, ewareki wareki_num, wareki_ymd and md should have correct values
         Failure/Error: expect(wareki(*ymd)).to eq jy
    
         NoMethodError:
           undefined method `wareki' for #<RSpec::ExampleGroups::ApplicationHelper::GenerateDateStrings:0x00007f896b5e6248>
         # ./spec/helpers/application_helper_spec.rb:15:in `block (4 levels) in <top (required)>'
         # ./spec/helpers/application_helper_spec.rb:14:in `each'
         # ./spec/helpers/application_helper_spec.rb:14:in `block (3 levels) in <top (required)>'
    
    Finished in 0.00677 seconds (files took 2.45 seconds to load)
    1 example, 1 failure
    
    Failed examples:
    
    rspec ./spec/helpers/application_helper_spec.rb:13 # ApplicationHelper generate date strings wareki, ewareki wareki_num, wareki_ymd and md should have correct values
    
    /Users/hkob/.rbenv/versions/2.5.3/bin/ruby -I/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/lib:/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-support-3.8.0/lib /Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb failed
  7. テストが多くなると現在の表示(progress)の方がよいが,初心者はテストが少ない最初のうちは .rspec の設定を書き換えて documentation 表示にしてみるとよい(2行目を追加).
  8. --require spec_helper
    --format documentation
  9. documentation に変えた時の spec 表示はこうなる.エラーのところにも書かれているが,こちらの方が describe や context による階層構造が見やすい.
  10. ApplicationHelper
      generate date strings
        wareki, ewareki wareki_num, wareki_ymd and md should have correct values (FAILED - 1)

テストを通す

  1. 先ほど記載した spec が通るように実装を記載する.実装することが目的ではないので,別のシステムで使っている実装をそのままコピーしてテストを通すこととする.修正した app/helpers/applicaton_helper.rb は以下のようになった.
  2. module ApplicationHelper
      # @param [Fixnum] y 年
      # @param [Fixnum] m 月 (12)
      # @param [Fixnum] d 日 (31)
      # @return [Array<String, Fixnum>] 和暦記号と年度の配列 (例: H21, H1, S43)
      # @note 昭和は64年1月7日まで,1月8日以降は平成
      def warekiSub(y, m, d)
        return [ 'H', y - 1988 ] if y > 1989
        return [ 'H', 1 ] if y == 1989 && m > 1
        return [ 'H', 1 ] if y == 1989 && m == 1 && d > 7
        return [ 'S', y - 1925 ]
      end
    
      # @param [Fixnum] y 年
      # @param [Fixnum] m 月 (12)
      # @param [Fixnum] d 日 (31)
      # @return [String] 和暦年度文字列 (例: 平成21, 平成元, 昭和43).
      def wareki(y, m = 12, d = 31)
        str, y = warekiSub(y, m, d)
        { 'H' => '平成', 'S' => '昭和' }[str] + ((y == 1) ? "元" : y.to_s)
      end
    
      # @param [Fixnum] y 年
      # @param [Fixnum] m 月 (12)
      # @param [Fixnum] d 日 (31)
      # @return [String] 英文和暦記号年度文字列 (例: H21, H1, S43) .
      def ewareki(y, m = 12, d = 31)
        warekiSub(y, m, d).join('')
      end
    
      # @param [Fixnum] y 年
      # @param [Fixnum] m 月 (12)
      # @param [Fixnum] d 日 (31)
      # @return [Fixnum] 和暦年度 (21, 1, 43)
      def wareki_num(y, m = 12, d = 31)
        warekiSub(y, m, d)[1]
      end
    
      # @param [Fixnum] y 年
      # @param [Fixnum] m 月 (12)
      # @param [Fixnum] d 日 (31)
      # @return [String] 和暦年度月日 (例: 平成21年12月7日, 平成元年1月7日, 昭和43年4月25日)
      def wareki_ymd(y, m = 12, d = 31)
        "#{wareki(y, m, d)}年#{m}月#{d}日"
      end
    
      # @param [Fixnum] m 月 (12)
      # @param [Fixnum] d 日 (31)
      # @return [String] 月日 (例: 12月7日, 1月7日, 4月25日)
      def md(m = 12, d = 31)
        "#{m}月#{d}日"
      end
    end
  3. spec を実行すると無事にテストの通過を確認できた.
  4. $ bin/rails spec
    /Users/hkob/.rbenv/versions/2.5.3/bin/ruby -I/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/lib:/Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-support-3.8.0/lib /Users/hkob/rails/attendance/vendor/bundle/ruby/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb
    
    ApplicationHelper
      generate date strings
        wareki, ewareki wareki_num, wareki_ymd and md should have correct values
    
    Finished in 0.00728 seconds (files took 2.51 seconds to load)
    1 example, 0 failures

長くなったので今日はここまで