如何编写和继承ActionController :: TestCase的抽象子类

时间:2012-01-17 01:26:20

标签: ruby-on-rails ruby testing abstract-class testunit

我有一堆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>'
===================================================================================================

我听说其他测试框架和宝石可以为测试的元编程提供机制,所以也许我会以完全错误的方式解决这个问题。但是我尝试了几件事,看了RSpeccouldashouldacontextcontest ......我仍然看不到实现我所追求的方式。有任何想法吗?谢谢!

1 个答案:

答案 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 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,但我还不太清楚它还不确定。