【问题标题】:Generate Rake test tasks dynamically (based on existing test files) in a Rakefile在 Rakefile 中动态生成 Rake 测试任务(基于现有测试文件)
【发布时间】:2012-03-02 19:37:36
【问题描述】:

我正在根据 Rakefile 中的现有测试文件动态生成测试任务。假设您有各种以模式test_<name>.rb 命名的单元测试文件。所以我正在做的是创建一个以“test”命名空间内的文件名命名的任务。 使用下面的代码,我可以使用 rake test:<name> 调用所有测试

require 'rake/testtask'

task :default => 'test:all'

namespace :test do

  desc "Run all tests"
  Rake::TestTask.new(:all) do |t|
    t.test_files = FileList['test_*.rb']
  end

  FileList['test_*.rb'].each do |task|
    name = task.gsub(/test_|\.rb\z/, '')
    desc "Run #{name} tests"
    Rake::TestTask.new(:"#{name}") do |t|
      t.pattern = task
    end
  end

end

上面的代码有效,只是对于简单的任务生成来说代码似乎太多了。 而且我还没有想出一种将一些描述文本打印到控制台的方法,例如puts "Running #{name} tests:"

还有比上述方法更优雅的方法吗?

编辑: 我真正期望得到的是一个替代循环来动态定义任务,但我猜 rake 库没有提供任何帮助,所以我坚持循环。

【问题讨论】:

  • 从@ScottJShea 的提示中编辑了代码
  • 对于那些不知道的人,Jim Weirich 是 Rake 项目的作者,我感谢他在下面回答我的问题。

标签: ruby rake-task rakefile


【解决方案1】:

这是在 Rake 中使用规则解决问题的另一种方法。

每当 rake 想要构建“X”时,一个 rake 规则就会启动,它会找到一条规则,上面写着“构建 X,使用 Y”。我们将设置一个规则,当有人以“test:XXX”格式指定目标时触发,它将尝试使用名为“test/test_XXX.rb”的文件。

require 'rake/testtask'

task :default => 'test:all'

TEST_FILES = FileList['test/test_*.rb']

namespace :test do
  desc "Run all tests"
  Rake::TestTask.new(:all) do |t|
    t.test_files = TEST_FILES
  end

  rule /^test:/ => lambda { |tn| "test/test_%s.rb" % tn.gsub(/^test:/,'') } do |rule|
    ruby rule.source
  end
end

假设您有一个名为“test/test_my_code.rb”的测试文件。要执行该测试文件,只需键入:

rake test:my_code

只要有任何其他任务无法匹配的以“test:”开头的目标,就会触发该规则。然后它查找由 lamdba 函数给出的文件。 lambda 将目标名称“test:XXX”转换为文件名“test/test_XXX.rb”。如果文件名存在,则执行规则主体。

规则的主体只是将测试文件作为可执行文件运行。这通常足以运行单个文件的测试。如果您需要将库路径(例如“lib”)添加到测试的加载路径,您可以将规则体更改为类似

ruby "-Ilib", rule.source

这与显式循环解决方案之间的另一个区别是 rake 不会打印出规则的描述,因此“rake -T”输出不会在其输出中包含单独的测试。

我不知道这是否比原版更好,但它确实为您提供了一些选择。

【讨论】:

  • 感谢您接受我的请求,为我们提供一些启示。我已经看到了替代规则,但直到现在我才完全理解它。我接受了你的回答,因为它是唯一一个展示循环解决方案的替代方案。正如您所说,在我看来,它有一个主要缺点,因为它不会为 rake -T 生成列表,并且 TestTask 类不允许自定义描述。我打算在 github 上 fork rake repo 并查看一下。同时,在 Rake::TestTask 中有这样的限制吗?再次感谢您接受我的挑战。
【解决方案2】:

我的想法:

namespace :test do

  FileList['test_*.rb'].each do |rakefile|
    name = rakefile.gsub(/test_|\.rb\z/, '')

    desc "Run #{name} tests"
    task name do 
      require_relative rakefile
    end
    #Define default task for :test
    task :default => name
  end

end

desc "Run all tests"
task :test => 'test:default'
task :default => 'test'

但我不确定将Rake::TestTask.new 替换为require_relative 是否是个好主意。

我的解决方案包含另一个更改: 我替换tasktest:allwithtest:defaultand define a new tasktest`。

所以你得到了rake -T 的以下结果:

rake test    # Run all tests
rake test:1  # Run 1 tests
rake test:2  # Run 2 tests

如果要运行所有测试,需要rake test,具体测试可以用rake test:<name>完成


您也可以通过Rake::TestTask.new 制作它

require 'rake/testtask'
namespace :test do

  FileList['test_*.rb'].each do |rakefile|
    name = rakefile.gsub(/test_|\.rb\z/, '')

    Rake::TestTask.new(:"#{name}") do |t|
      t.pattern = rakefile
    end
    #Define default task for :test
    task :default => name
  end

end

desc "Run all tests"
task :test => 'test:default'
task :default => 'test'

rake -T 我得到:

rake test    # Run all tests
rake test:1  # Run tests for 1
rake test:2  # Run tests for 2

描述已生成。

您可以添加:

    desc 'Alternative description'
    task name

然后你得到:

rake test    # Run all tests
rake test:1  # Run tests for 1 / Alternative description
rake test:2  # Run tests for 2 / Alternative description

如果你想改变你可以添加的文字

    #replace description
    Rake.application[name].comment.replace("Run #{name} tests")

Rake::TestTask.newend 之后。那是丑陋的代码,但Rake::TestTask 不允许更改描述(这是可能的,但它会是对类的修改)。

【讨论】:

  • 感谢您的贡献,我不太喜欢使用 require_relative 代替 Rake::TestTask.new 的想法,因为 TestTask 添加了将来可能需要的更多功能(请参阅rake.rubyforge.org/classes/Rake/TestTask.html) .但是,我很欣赏 rake 测试而不是 rake test:all 的建议。请检查我的问题编辑以获取更多考虑。
  • 我扩展了我的答案。 FileList['test_*.rb'] 仍有一个循环,但只有一个。第二个是通过默认任务完成的。
  • 请注意,您通过将每个单独的测试作为要求添加到默认任务来实现运行所有测试的目标。 +1 用于显示运行所有测试的不同方式,无论是一起运行还是单独运行。谢谢
【解决方案3】:

嗯……我所做的一个改变是我能想到的。不确定它是否值得一个完整的答案,但我想确保我没有破坏任何东西。您也可以尝试将其发布到Stack Exchange's Code Review

require 'rake/testtask'

task :default => 'test:all'

namespace :test do

  desc "Run all tests"
  Rake::TestTask.new(:all) do |t|
    t.test_files = FileList['test_*.rb']
  end

  FileList['test_*.rb'].each do |task|
    name = task.gsub(/test_|\.rb\z/, '')
    desc "Run #{name} tests"
    Rake::TestTask.new(:"#{name}") do |t|
      t.pattern = task
    end
  end

end

【讨论】:

  • 我很感激这个提示,尽管它并不是我想要的那种改进。
猜你喜欢
  • 2010-11-18
  • 2015-12-29
  • 2011-05-06
  • 1970-01-01
  • 2011-10-17
  • 2012-04-16
  • 2011-12-19
  • 2018-06-19
  • 1970-01-01
相关资源
最近更新 更多