Rails ActiveSupport :: TestCase - 如何动态定义测试及其辅助方法?

时间:2018-05-03 15:05:37

标签: ruby-on-rails ruby metaprogramming minitest

我正在构建一些抽象到我的Rails(5.2)测试中,因为我想用不同的参数集多次运行相同的测试。

我可以成功创建一个帮助程序来动态生成测试类。在我的test_helper.rb中看起来如下所示:

class << self
    def test_configs(configs: DEFAULT_CONFIGS, &block)
        configs.each do |c|
            Class.new(ActiveSupport::TestCase) { yield(c) }
        end
    end
end

我可以在任何给定的测试文件中使用此帮助程序,如下所示:

require 'test_helper'

class SampleTest < ActiveSupport::TestCase
  test_configs do |c|
    test "#{c[:name]} - something is true" do
      puts "#{c[:name]}" => # Correctly outputs c[:name]
    end
  end
end

"#{c[:name]}"根据&#34; config&#34;为每次迭代正确插值。从助手传递。到目前为止一切都很好。

我有一个问题,即在c方法本身或单个测试文件中创建一些也使用变量test_configs的辅助方法。

以下任何一项都无法在传递给测试标题的c变量与测试本身内发生的变量之间进行一致匹配:

# Approach 1
class SampleTest < ActiveSupport::TestCase
  test_configs do |c|
    def config_name
      "#{c[:name]}" # => undefined local variable or method `c'
    end

    test "#{c[:name]} - something is true" do
      puts "#{config_name}"
    end
  end
end

# Approach 2
class SampleTest < ActiveSupport::TestCase
  test_configs do |c|
    define_method("config_name") {
      "#{c[:name]}"
    }

    test "#{c[:name]} - something is true" do
      puts "#{config_name}" # => Uses only the last definition 
    end
  end
end

# Approach 3
class << self
    def test_configs(configs: DEFAULT_CONFIGS, &block)
        configs.each do |c|
            define_method "config_name" do # (same with def config_name)
                "#{c[:name]}"
            end
            Class.new(ActiveSupport::TestCase) { yield(c) }
        end
    end
end
# => undefined local variable or method `config_name'

我如何正确地注射&#34;注射&#34;使用传递的变量的方法?

1 个答案:

答案 0 :(得分:1)

正确的方法类似于我的问题中的第3个方法,但yield必须替换为instance_eval,以便块中的代码继承我正在创建的新类的上下文:

# test_helper.rb
class ActiveSupport::TestCase
  class << self
    def test_configs(configs: DEFAULT_CONFIGS, &block)
      configs.each do |c|
        Class.new(ActiveSupport::TestCase) {
          define_singleton_method "config_title" do
            "#{c[:name]}".capitalize
          end

          define_method "config_name" do
            "#{c[:name]}"
          end

          instance_eval(&block)
        end
      end
    end
  end
end


# sample_test.rb
require 'test_helper'

class SampleTest < ActiveSupport::TestCase
  test_configs do
    test "#{config_title} - something is true" do # => Correctly invokes the class method defined above
      puts "#{config_name}" # => Correctly invokes the instance method defined above
    end
  end
end

正如您所看到的,还有一个区别是必须定义类方法才能在测试标题中使用,而实例方法必须定义为在测试中使用。

如果必须将c传递给块(例如,为了在块本身中定义其他方法),instance_exec应该是可行的方法:docs