上次測試Modeul的問題還沒有解決,但是下面的還要繼續,這次來測試Controller。
1.在test\functional目錄下,rails已經為我們的controller生成了對應的測試文件,要注意application_controller不會生成測試文件。我們以控制登錄的LoginController為例,打開login_controller_test.rb,內容如下:
require File.dirname(__FILE__) + '/../test_helper' require 'login_controller' # Re-raise errors caught by the controller.class LoginController; def rescue_action(e) raise e end; end class LoginControllerTest < Test::Unit::TestCase def setup @controller = LoginController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end # Replace this with your real tests.def test_truth assert true end end
我們看到,在setup方法裡,定義了三個對象@controller和@request和@response,這樣,我們就可以在不接入webserver或network的情況下進行測試了。
2.我們來把其中的test_truth方法替換成下面的代碼:
def test_index get :index assert_response :success end
其中,get方法模擬發出一個web請求,請求的action是index,並且捕捉響應(response),然後由assert_response斷言來判斷響應是否成功。
現在運行測試:depot>ruby test/functional/login_controller_test.rb
會看到測試失敗了,命令行的輸出:
Expected response to be a <:success>, but was <302> 1 tests, 1 assertions, 1 failures, 0 errors
為什麼會這樣呢?回想一下,我們在前面的程序裡,在訪問index頁面時,要先判斷用戶是不是管理員。如果不是,就要跳轉到login頁面,在login_controller.rb裡:
before_filter :authorize, :except => :login
在application.rb文件裡,有authorize方法:
def authorize unless session[:user_id] flash[:notice] = "Please log in" redirect_to(:controller => "login", :action => "login") end end
3.好了,知道了原因,現在再寫一個測試:
def test_index_without_user get :index assert_redirected_to :action => "login" assert_equal "Please log in", flash[:notice] end
上面的代碼裡,我們嘗試請求index這個action,並且使用斷言判斷是否重定向到login,再判斷flash[:notice]裡的內容。
再次運行測試,命令行的輸出如下:
Loaded suite test/functional/login_controller_test Started .Finished in 0.062 seconds.1 tests, 3 assertions, 0 failures, 0 errors
可以看到所有的斷言都通過了。
但是還有一個問題,就是根據代碼,我們只使用了兩個斷言,但是提示信息卻顯示有三個斷言,這個問題是什麼原因還不太清楚,但是現在並不影響我們繼續下面的內容。
4.在我們的login頁面上,用戶輸入名字和密碼後,點擊login按鈕,這些信息將會發送個login這個action,然後創建一個User的實例來讓用戶登入。
在login_controller.rb裡的login方法裡:
@user = User.new(params[:user]) logged_in_user = @user.try_to_login
現在,我們來測試login這個action,在login_controller_test.rb中添加方法:
def test_login_with_invalid_user post :login, :user => {:name => 'fred', :password => 'opensesame'} assert_response :success assert_equal "Invalid user/password combination", flash[:notice] end
大家一定看到了,這是使用了一個不存在的用戶名和密碼來測試login。
運行測試,輸出結果如下:
Finished in 0.609 seconds.2 tests, 5 assertions, 0 failures, 0 errors
和上一個測試方法相對的,這裡代碼裡是兩個斷言,根據輸出也是兩個,挺奇怪的事情。
5.測試了不存在的用戶,現在我們來測試用戶存在的情況。
修改fixtures目錄下的users.yml文件,我們還使用上面的用戶名和密碼:
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html fred: id: 1 name: fred hashed_password: <%= Digest::SHA1.hexdigest('abracadabra') %>
然後給login_controller_test.rb添加fixtures :users,指定使用users.yml文件。
再添加方法:
def test_login_with_valid_user post :login, :user => {:name => 'fred', :password => 'abracadabra'} assert_redirected_to :action => "index" assert_not_nil(session[:user_id]) user = User.find(session[:user_id]) assert_equal 'fred', user.name end
在這裡方法裡,我們指定了和yml文件中定義的用戶名相一致的數據,並且判斷是否定位到index,再判斷用戶的id是否已經保存在session中。
根據前面測試Model時候的內容,我們在yml文件中定義的數據會被加入到數據庫中,這樣,這個方法裡的三個斷言都應該通過才對。
OK了,運行測試,輸出結果:
Finished in 0.125 seconds.3 tests, 8 assertions, 0 failures, 0 errors
嗯,所有的斷言都通過了。
好了,這次就到這裡。