2012-01-17 22 views
4

我有一堆Rails 3.1控制器,它們都具有非常相似的測試要求。我已經提取出了公共代碼(所有Test :: Unit樣式),例如以下三項測試在所有他們完全可重複使用:如何編寫並繼承ActionController的抽象子類:: TestCase

def create 
    new_record = { field_to_update => new_value } 
    create_params = { :commit => "Create", :record => new_record } 
    post :create, create_params 
    end 

    test "should_not_create_without_login" do 
    assert_no_difference(count_code) do create; end 
    assert_need_to_log_in 
    end 

    test "should_not_create_without_admin_login" do 
    login_as_non_admin 
    assert_no_difference(count_code) do create; end 
    assert_needs_admin_login 
    end 

    test "should_create" do 
    login_as_admin 
    assert_difference(count_code) do create; end 
    assert_redirected_to list_path 
    end 

,我想做的是它可以在從ActionController::TestCase繼承抽象類去。然後,每個功能測試只需要重寫抽象方法,結果令人愉快地小而乾淨,例如,

class Admin::AvailabilitiesControllerTest < Admin::StandardControllerTest 
    tests Admin::AvailabilitiesController 

    def model   ; Availability    end 
    def id_to_change ; availabilities(:maybe).id end 
    def field_to_update; :value     end 
    def new_value  ; 'maybe2'     end 
    def list_path  ; admin_availabilities_path end 
end 

然而,當我嘗試這一點,看來該框架嘗試直接從抽象類運行測試方法,從繼承的類,而不是:

E               
=================================================================================================== 
Error:              
test_should_not_create_without_login(Admin::ControllerTestBase): 
NoMethodError: undefined method `model' for test_should_not_create_without_login(Admin::ControllerTestBase):Admin::ControllerTestBase 
    test/lib/admin_controller_test_base.rb:7:in `count_code' 
    test/lib/admin_controller_test_base.rb:68:in `block in <class:ControllerTestBase>' 
=================================================================================================== 

聽說其他測試框架和寶石可以爲測試的元編程提供機制,所以也許我會用完全錯誤的方式來解決這個問題。但我已經嘗試了幾件事情,看着RSpec,coulda,shoulda,context,contest ......我仍然看不到一種方法來實現我所追求的。有任何想法嗎?謝謝!

回答

3

我終於明白了這一點 - 一旦我意識到這是一個普通的Ruby Test :: Unit問題而不是Rails測試問題,一個快速谷歌即時顯示How do I inherit abstract unit tests in Ruby?已經有一個很好的答案。 lib/active_support/test_case.rb

test "something should behave in a certain way" do 
    ... 
end 

而不是

def test_something_should_behave_in_a_certain_way" do 
    ... 
end 

我找到了答案,這個的ActiveSupport代碼庫本身,:那麼,唯一缺少的部分是能夠使用的語法糖

extend ActiveSupport::Testing::Declarative 

該模塊defines test as a class methodwhich is why extend is required rather than include)。

因此,完整的解決方案是這樣的:

# tests/functional/admin/availabilities_controller_test.rb 

class Admin::AvailabilitiesControllerTest < ActionController::TestCase 
    tests Admin::AvailabilitiesController 
    include Admin::ControllerTests 

    # non-reusable tests and helper methods specific to this 
    # controller test go here 
end 


# lib/admin/controller_tests.rb 

module Admin::ControllerTests 
    extend ActiveSupport::Testing::Declarative 

    test "this test can be reused by anything which includes this module" do 
    ... 
    end 
end 

這種基於模塊的方法的缺點是,你不能覆蓋包括測試。我想這只是Test :: Unit的一個基本限制 - 也許最好的答案是轉向RSpec,但我還不太清楚它的確定性。

+1

謝謝,非常有幫助。遵循您的建議,我將所有可重用的邏輯提取到測試模塊中,並將它們放置在test/lib目錄下。 Rails.env.test時,我還添加了條件自動加載test/lib到config.autoload_paths?是真的。這樣,只有在運行測試時才加載此代碼。 – 2013-05-16 21:43:49