與時間有關的測試

有時候需要確認代碼在特定日期仍正確執行,或是根據時間顯示正確的資料,就會需要能夠操縱時間。

Rails 內建 ActiveSupport::Testing::TimeHelpershttp://api.rubyonrails.org/classes/ActiveSupport/Testing/TimeHelpers.html

Ruby 專案則可以使用 Timecop 這個專案:https://github.com/travisjeffery/timecop

ActiveSupport::Testing::TimeHelpers

主要提供了三個方法:

  • travel_to(date_or_time) { ... }

移動到特定的時間點。

  • travel(duration, &block)

移動一段時間。

  • travel_back()

從特定時間點回到當下。

配置

spec/spec_helper.rb 加入:

RSpec.configure do |config|
  config.include ActiveSupport::Testing::TimeHelpers
end

在測試裡便可以使用上面提到的三個方法。

例子

移動到特定時間點檢查 View 的內容,比如:

# app/views/layouts/application.html.erb
...

<footer>
  <p>&copy; <%= Time.current.year %> 深圳新生教育科技有限公司</p>
</footer>

在頁面上顯示:

© 2016 深圳新生教育科技有限公司

可以寫 Controller 測試來檢查

RSpec.describe ApplicationController do
  describe "#index" do
    def do_request
      get :index
    end

    it "should be success" do
      do_request

      expect(response).to be_success
    end

    context "from the future" do
      render_views

      it "display year correctly" do
        travel_to Time.new(2046, 1, 1, 0, 0, 0) do
          do_request

          expect(response.body).to match /© 2046 深圳新生教育科技有限公司/
        end
      end
    end
  end
end

render_viewRSpec::Rails 提供的方法,預設 Controller spec 不會 render view,這裡我們要檢查 view 的內容所以要 render。

travel_to Time.new(2046, 1, 1, 0, 0, 0) do
  # 這裡面時間是 2046 年
end

view rendered 的內容可以在 response.body 拿到。

例子

比如 Coupon 在七天後過期。

class Coupon
  def valid?
    (Time.current - created_at) < 7.days
  end
end
RSpec.describe Coupon do
  describe "#valid?" do
    context "within a week" do
      it "returns true" do
        coupon = Coupon.new

        expect(coupon.valid?).to be true
      end
    end

    context "after a week" do
      it "returns false" do
        coupon = Coupon.new

        travel 8.days do
          expect(coupon.valid?).to be false
        end

        travel_back # 記得測試跑完要把時間還原
      end
    end
  end
end

travel_to 接受特定日期,而 travel 則是接受一段時間。

看怎麼樣搭配使用可以提高測試的可讀性,但使用 travel 要記得使用 travel_back,不然其他測試的時間就亂了。

例子 3

特賣會標題!

class Sale
  # Sale on 2016/10/12
  def title
    "Sale on #{Time.current.strftime('%Y/%m/%d')}"
  end
end

strftime doc.

RSpec.describe Sale do
  describe "#title" do
    it "shows correct date in title" do
      travel_to Time.zone.local(2046, 1, 15, 0, 0, 0) do
        result = Sale.new.title

        expect(result).to eq "Sale on 2046/1/15"
      end
    end
  end
end

學會了 ActiveSupport::Testing::TimeHelpers 怎麼用以後,Timecop 依樣畫葫蘆請參考 README:https://github.com/travisjeffery/timecop

results matching ""

    No results matching ""