模型測試


Rails預設有三個執行環境,即development、test與production,有幾個檔案與這三個執行環境有關,config/database.yml中預設定義了三個執行環境所使用的資料庫設定:

  • database.yml
development:
  adapter: sqlite3
  database: db/development.sqlite3
  pool: 5
  timeout: 5000

test:
  adapter: sqlite3
  database: db/test.sqlite3
  pool: 5
  timeout: 5000

production:
  adapter: sqlite3
  database: db/production.sqlite3
  pool: 5
  timeout: 5000

建立新專案時,預設就會產生development.sqlite3與test.sqlite3,後者是作為測試時的資料庫,其中的資料在每次測試時都會根據相關設定重建,確保每次測試的資料來源一致。config/environments中也分別有development.rb、production.rb、test.rb,分別作為三個環境的組態檔案。

Rails的應用程式通常與資料庫有密切互動,所以實際上進行測試時若存在相關模型測試檔案,就必須在資料庫中有對應的表格定義。例如 認識 Rails 測試 中的例子,如果已像 觀摩 Scaffold 中產生了Message模型,即使你指定執行rational_number_test.rb,也會發生以下錯誤,告訴你沒有對應的表格:

~gossip\$ ruby -Itest test/unit/rational_number_test.rb
Loaded suite test/unit/rational_number_test
Started

RationaNumberlTest:
    ERROR rational number addition (0.00s)
          ActiveRecord::StatementInvalid: Could not find table 'messages'
          /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-3.1.3/lib/active_record/connection_adapters/sqlite_adapter.rb:439:in `table_structure'

    ERROR rational number subtraction (0.00s)
          ActiveRecord::StatementInvalid: Could not find table 'messages'
          /usr/local/lib/ruby/gems/1.9.1/gems/activerecord-3.1.3/lib/active_record/connection_adapters/sqlite_adapter.rb:439:in `table_structure'


Finished in 0.007304 seconds.

2 tests, 0 assertions, 0 failures, 2 errors, 0 skips


在執行測試之前,得執行過rake db:migrate,產生schema.rb之後,再執行rake db:test:load,將schema.rb中的結構記錄載入測試資料庫,之後若有任何遷移變更,可執行rake db:test:prepare,將遷移變更載入測試資料庫,再執行相關測試。

來看看一個測試Message的例子:
  • message_test.rb
require 'test_helper'

class MessageTest < ActiveSupport::TestCase
    test "should not save message without name" do
        assert !Message.new.save, "Saved the message without name"
    end
end

這測試了Message儲存時,必須驗證name是否設定,接著修改Message定義
  • message.rb
class Message < ActiveRecord::Base
    validates :name, :presence => true
end
 
如果在測試之前,資料庫中必須有某些既存的資料,那麼你可以定義fixtures,也就是範例資料,這是test/fixtures資料夾中定義YAML,例如:
  • messages.yml
justin:
  name: Justin Lin
  title: About Java TWO 2012
  content: When will it be?
 
momor:
  name: Momor Huang
  title: Kindle DX Arrived
  content: As title

Rails預設會載入test/fixtures中的YAML,原因在於 認識 Rails 測試 中看過的,test_helper.rb中定義了fixtures :all(如果只想指定載入特定YAML,可以指定如fixtures :messages),以便進行單元測試與功能測試,載入的過程有:
  1. 移除資料庫表格中對應fixtures的資料
  2. 將fixtures中的資料重新載入表格
  3. 將fixtures中的資料載入對應變數

以上例而言,可以透過messages方法來取得對應的fixture,例如messages(:justin)可取得YAML中定義的資料,取得的物件是模型的型態,例如messages(:justin)就是取得Message物件。

fixture定義檔中,也可以使用Erb,內嵌的Ruby程式碼會先被處理。例如:

  • messages.yml
<% sample_content = "blah..blah" %>
justin: name: Justin Lin title: About Java TWO 2012 content: <%= sample_content %> momor: name: Momor Huang title: Kindle DX Arrived content: <%= sample_content %>

實際上除了如上使用ruby指令逐一執行測試之外,也可以使用rake test執行所有已定義的測試,如果僅想執行單元測試,可以執行rake test:units,這會執行test/unit中定義的單元測試,類似地,執行rake test:functionals可執行test/functional中定義的功能測試,執行rake test:integration可執行test/integration中定義的功能測試。