
- 學習 Ruby on Rails
- Rails 2.1 首頁
- Rails 2.1 簡介
- Rails 2.1 安裝
- Rails 2.1 框架
- Rails 2.1 目錄結構
- Rails 2.1 示例
- Rails 2.1 資料庫設定
- Rails 2.1 Active Records
- Rails 2.1 資料遷移
- Rails 2.1 控制器
- Rails 2.1 檢視
- Rails 2.1 佈局
- Rails 2.1 腳手架
- Rails 2.1 和 AJAX
- Rails 2.1 上傳檔案
- Rails 2.1 傳送郵件
- 高階 Ruby on Rails 2.1
- Rails 2.1 RMagick 指南
- Rails 2.1 基本 HTTP 認證
- Rails 2.1 錯誤處理
- Rails 2.1 路由系統
- Rails 2.1 單元測試
- 高階 Ruby on Rails 2.1
- Rails 2.1 提示與技巧
- 快速參考指南
- 快速參考指南
- Ruby on Rails 2.1 有用資源
- Ruby on Rails 2.1 - 資源
- Ruby on Rails 2.1 - 討論
Ruby on Rails 2.1 - 單元測試
簡介
在繼續之前,讓我們快速瀏覽幾個定義:
測試 - 它們是測試應用程式,產生一致的結果,並證明 Rails 應用程式符合預期。測試與實際應用程式同時開發。
斷言 - 這是評估物件(或表示式)是否符合預期結果的一行程式碼。例如 - 此值 = 該值?此物件是否為 nil?
測試用例 - 這是一個從 Test::Unit::TestCase 繼承的類,包含由上下文相關的測試組成的測試策略。
測試套件 - 這是測試用例的集合。執行測試套件時,它將依次執行屬於它的每個測試。
Rails 測試
當您執行輔助指令碼script/generate 來建立控制器和模型時,Rails 會生成單元測試和功能測試的框架。透過為編寫的功能填寫框架中的測試,您可以獲得相當不錯的測試覆蓋率。在 Rails 應用程式中需要測試兩個重要方面:
測試模型
測試控制器
本教程將簡要介紹這兩種測試。因此,讓我們建立一個testapp來理解這個概念。
C:\ruby> rails -d mysql testapp
資料庫設定
到目前為止,我們只使用了 Rails 應用程式的開發資料庫,但現在您需要確保測試資料庫也已建立,並且您的 config/database.yml 檔案中的相應部分已正確設定。
讓我們建立開發和測試資料庫,如下所示:
mysql> create database testapp_test; Query OK, 1 row affected (0.01 sec) mysql> create database testapp_development; Query OK, 1 row affected (0.01 sec) mysql> use testapp_test; Database changed mysql> grant all privileges on testapp_test.* to 'root'@'localhost' identified by 'password'; Query OK, 0 rows affected (0.00 sec) mysql> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec)
配置 database.yml
請按如下方式配置您的 config/database.yml:
development: adapter: mysql encoding: utf8 database: testapp_development username: root password: password host: localhost test: adapter: mysql encoding: utf8 database: testapp_test username: root password: password host: localhost production: adapter: mysql encoding: utf8 database: testapp_production username: root password: password host: localhost
生成遷移
假設您有一個包含書籍的表,其中包括它們的標題、價格和簡短描述。以下遷移設定此表:
testapp > ruby script/generate migration books
現在修改 testapp/db/migrate/20080616170315_books.rb 檔案,如下所示:
class Books < ActiveRecord::Migration def self.up create_table :books do |t| t.string :title, :limit => 32, :null => false t.float :price t.text :description t.timestamp :created_at end end def self.down drop_table :books end end
現在執行遷移,如下所示:
testapp > rake db:migrate
這將在 testapp_development 資料庫中建立books表。此後,我們需要使用rake命令設定您的測試資料庫,如下所示:
C:\ruby\testapp > rake db:test:clone_structure
這會將testapp_development資料庫克隆到testapp_test資料庫中。這意味著開發資料庫中擁有的任何資料,現在測試資料庫中也會擁有相同的資料。
測試模型
當您使用 generate 指令碼生成模型時,Rails 還會在測試目錄中為該模型生成一個單元測試指令碼。它還會建立一個fixture,一個包含要載入到 testapp_test 資料庫中的測試資料的YAML檔案。這是單元測試將針對其執行的資料:
testapp > ruby script/generate model Book exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/book.rb create test/unit/book_test.rb create test/fixtures/books.yml create db/migrate create db/migrate/20080616164236_create_books.rb
當您在模型類中編寫程式碼時,您將在這些檔案中編寫相應的測試。因此,讓我們使用 YAML 在 test/fixtures/books.yml 中建立兩條測試書籍記錄,如下所示:
perl_cb: id: 1 title: 'Ruby Tutorial' price: 102.00 description : 'This is a nice Ruby tutorial' java_cb: id: 2 title: 'Java Programming' price: 62.00 description : 'Java Programming for the beginners'
現在讓我們用以下程式碼替換單元測試檔案 test/unit/book_test.rb 中的現有程式碼:
require File.dirname(__FILE__) + '/../test_helper' class BookTest < ActiveSupport::TestCase fixtures :books def test_book perl_book = Book.new :title => books(:perl_cb).title, :price => books(:perl_cb).price, :description => books(:perl_cb).description, :created_at => books(:perl_cb).created_at assert perl_book.save perl_book_copy = Book.find(perl_book.id) assert_equal perl_book.title, perl_book_copy.title perl_book.title = "Ruby Tutorial" assert perl_book.save assert perl_book.destroy end end
最後,執行測試方法,如下所示:
testapp > ruby test/unit/book_test.rb
以下是成功測試用例的輸出:
testapp > ruby test/unit/book_test_crud.rb Loaded suite ./test/unit/book_test Started . Finished in 0.0625 seconds. 1 tests, 4 assertions, 0 failures, 0 errors
讓我們分析一下這裡發生了什麼:
BookTest 方法首先使用文字 fixture/books.yml 中第一條記錄中的標題和其他欄位建立一個新的 Book 物件。生成的物件儲存在 perl_book 例項變數中。
第一個斷言測試儲存 Book 物件是否成功。
接下來,使用find方法檢索 book 物件並將其儲存在名為 perl_book_copy 的另一個例項變數中。下一個斷言測試此檢索的成功,它比較兩個 book 物件的標題。此時,我們已經測試了建立和讀取資料庫記錄的能力。
解決方案透過為儲存在 perl_book 中的物件分配一個新標題來測試更新,然後斷言更改儲存成功。
最後,測試了銷燬 Book 物件的能力。
這就是我們測試 Rails 模型的方法。
測試控制器
控制器測試也稱為功能測試。功能測試測試控制器的以下型別功能:
- 響應是否按預期重定向?
- 是否呈現了預期的模板?
- 路由是否符合預期?
- 響應是否包含預期的標籤?
Rails 框架支援五種型別的請求:
- get
- post
- put
- head
- delete
要編寫功能測試,您需要模擬控制器將處理的五種 HTTP 請求型別中的任何一種。
請求型別“get”和“post”在控制器測試中最常用。所有這些方法都接受四個引數:
- 控制器的操作
- 可選的請求引數雜湊
- 可選的會話雜湊
- 可選的快閃記憶體雜湊
在本教程中,我們將看到如何使用get方法測試我們的控制器。您可以透過類似的方式測試其餘的方法。
當您使用 generate 生成控制器時,Rails 會如下建立控制器的功能測試指令碼:
testapp > ruby script/generate controller Book exists app/controllers/ exists app/helpers/ create app/views/book exists test/functional/ create app/controllers/book_controller.rb create test/functional/book_controller_test.rb create app/helpers/book_helper.rb
當您在控制器類中編寫程式碼時,您將在這些檔案中編寫相應的測試。在此之前,讓我們在app/controllers/book_controller.rb中定義控制器函式list、show和search,如下所示:
class BookController < ApplicationController def list @book_pages, @books = paginate :books, :per_page => 10 end def show @book = Book.find(params[:id]) end def search @book = Book.find_by_title(params[:title]) if @book redirect_to :action => 'show', :id => @book.id else flash[:error] = 'No such book available' redirect_to :action => 'list' end end end
注意 - 您將需要show和list方法的兩個檢視模板。您可以定義這些檢視並對其進行測試,但現在,我們將不定義這些檢視繼續進行。
現在讓我們重用位於test/fixtures/books.yml檔案中的測試夾具,如下所示:
perl_cb: id: 1 title: 'Ruby Tutorial' price: 102.00 description : 'This is a nice Ruby tutorial' java_cb: id: 2 title: 'Java Programming' price: 62.00 description : 'Java Programming for the beginners'
將以下test_search_book和test_search_not_found方法新增到test/functional/book_controller_test.rb中,以測試 Book Controller 的 search 操作的功能。
require File.dirname(__FILE__) + '/../test_helper' require 'book_controller' # Re-raise errors caught by the controller. class BookController def rescue_action(e) raise e end end class BookControllerTest < Test::Unit::TestCase fixtures :books def setup @controller = BookController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end def test_search_book get :search, :title => 'Ruby Tutorial' assert_not_nil assigns(:book) assert_equal books(:perl_cb).title, assigns(:book).title assert_valid assigns(:book) assert_redirected_to :action => 'show' end def test_search_not_found get :search, :title => 'HTML Tutorial' assert_redirected_to :action => 'list' assert_equal 'No such book available', flash[:error] end end
現在執行您的測試用例,如下所示:
testapp > ruby test/functional/book_controller_test.rb
它給出以下輸出:
Loaded suite test/functional/book_controller_test Started .. Finished in 0.422 seconds. 2 tests, 7 assertions, 0 failures, 0 errors
讓我們分析一下這裡發生了什麼:
setup方法是建立控制器、請求和響應物件的預設方法。它們將由 Rails 在內部使用。
第一個測試方法test_search_book向 search 操作生成一個get請求,傳入一個title引數。
接下來的兩個斷言驗證一個Book物件是否已儲存在名為@book的例項變數中,以及該物件是否透過可能存在的任何 Active Record 驗證。
第一個方法中的最終斷言測試請求是否已重定向到控制器的 show 操作。
第二個測試方法test_search_not_found執行另一個get請求,但傳入一個無效的標題。
第一個斷言測試是否發出了到list操作的重定向。
如果前面的斷言透過,則快閃記憶體雜湊中應該有一條訊息,您可以使用 assert_equal.. 進行測試。
要獲取有關斷言的更多資訊,請參考Rails 標準文件。
使用 Rake 進行測試
您可以使用rake實用程式來測試您的應用程式。下面列出了一些重要的命令。
$rake test - 測試所有單元測試和功能測試(以及整合測試,如果存在)。
$rake test:functionals - 執行所有功能測試。
$rake test:units - 執行所有單元測試。
$rake test:integration - 執行所有整合測試。
$rake test:plugins - 執行 ./vendor/plugins/**/test 中的所有測試。
$rake test:recent - 執行過去 10 分鐘內修改過的模型和控制器的測試:
$rake test:uncommitted - 對於 Subversion 中的專案,執行自上次提交以來在模型和控制器中發生更改的測試: