我有一堆Rails 3.1控制器,它们都有非常相似的测试要求。我已经提取出公共代码(所有Test :: Unit样式),例如以下三个测试完全可以在所有测试中重复使用:
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 ......我仍然看不到实现我所追求的方式。有任何想法吗?谢谢!
答案 0 :(得分:3)
我终于明白这一点 - 一旦我意识到这是一个普通的Ruby Test :: Unit问题而不是Rails测试问题,快速谷歌立刻透露How do I inherit abstract unit tests in Ruby?已经有了一个很好的答案。然后唯一缺失的部分是能够使用语法糖:
test "something should behave in a certain way" do
...
end
而不是
def test_something_should_behave_in_a_certain_way" do
...
end
我在ActiveSupport代码库本身的lib/active_support/test_case.rb
下找到了答案:
extend ActiveSupport::Testing::Declarative
此模块defines test
as a class method(which 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,但我还不太清楚它还不确定。