在 rspec(和 cucumber)中测试 rake 任务

发布于 2024-08-28 13:01:12 字数 1649 浏览 6 评论 0原文

我是 Ruby 新手,一直在尝试学习 Rake、RSpec 和 Cucumber。我找到了一些可以帮助我测试 Rake 任务的代码,但我无法让它正常工作。有人告诉我这里: http://blog.codahale.com /2007/12/20/rake-vs-rspec-fight/ 将其

def describe_rake_task(task_name, filename, &block)
  require "rake"

  describe "Rake task #{task_name}" do
    attr_reader :task

    before(:all) do
      @rake = Rake::Application.new
      Rake.application = @rake
      load filename
      @task = Rake::Task[task_name]
    end

    after(:all) do
      Rake.application = nil
    end

    def invoke!
      for action in task.instance_eval { @actions }
        instance_eval(&action)
      end
    end

    instance_eval(&block)
  end
end

放入我的 spec_helper.rb 文件中。

我已经设法取出这段代码并在我的黄瓜步骤中运行它,如下所示:

When /^I run the update_installers task$/ do
 @rake = Rake::Application.new
 Rake.application = @rake
 load "lib/tasks/rakefile.rb"
 @task = Rake::Task["update_installers"]

 for action in @task.instance_eval { @actions }
  instance_eval(&action)
 end

 instance_eval(&block)

 Rake.application = nil
end

但是当我尝试在 rspec 中运行时,出现以下错误。

“Rake 任务中的参数错误” install_grapevine 应该安装到 mygrapevine 目录'

参数数量错误(1 为 2) /spec/spec_helper.rb: 21:in instance_eval' /spec/spec_helper.rb: 21:in调用中的块!' /spec/spec_helper.rb: 20:in each' /spec/spec_helper.rb: 20:in 调用!' /spec/tasks/rakefile_spec.rb:12:in `块(2 级)中 '

不幸的是,我已经掌握了 Ruby 不到一周的时间,所以元编程的东西超出了我的能力范围。有人能指出我正确的方向吗?

I'm new to Ruby, and I've been trying to learn Rake, RSpec, and Cucumber. I found some code that will help me test my Rake tasks, but I'm having trouble getting it to work. I was told here: http://blog.codahale.com/2007/12/20/rake-vs-rspec-fight/ to drop this:

def describe_rake_task(task_name, filename, &block)
  require "rake"

  describe "Rake task #{task_name}" do
    attr_reader :task

    before(:all) do
      @rake = Rake::Application.new
      Rake.application = @rake
      load filename
      @task = Rake::Task[task_name]
    end

    after(:all) do
      Rake.application = nil
    end

    def invoke!
      for action in task.instance_eval { @actions }
        instance_eval(&action)
      end
    end

    instance_eval(&block)
  end
end

into my spec_helper.rb file.

I've managed to take this code out and run it in my cucumber steps like this:

When /^I run the update_installers task$/ do
 @rake = Rake::Application.new
 Rake.application = @rake
 load "lib/tasks/rakefile.rb"
 @task = Rake::Task["update_installers"]

 for action in @task.instance_eval { @actions }
  instance_eval(&action)
 end

 instance_eval(&block)

 Rake.application = nil
end

but when I try to get things working in rspec, I get the following error.

ArgumentError in 'Rake task
install_grapevine should install to
the mygrapevine directory'

wrong number of arguments (1 for 2)
/spec/spec_helper.rb: 21:in instance_eval'
/spec/spec_helper.rb: 21:in
block in invoke!'
/spec/spec_helper.rb: 20:in each'
/spec/spec_helper.rb: 20:in
invoke!'
/spec/tasks/rakefile_spec.rb:12:in `block (2 levels) in
'

Unfortunately, I've got just under a week of ruby under by belt, so the metaprogramming stuff is over my head. Could anyone point me in the right direction?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

左耳近心 2024-09-04 13:01:13

这对我有用:(Rails3/Ruby 1.9.2)

When /^the system does it's automated tasks$/ do    
  require "rake"
  @rake = Rake::Application.new
  Rake.application = @rake
  Rake.application.rake_require "tasks/cron"
  Rake::Task.define_task(:environment)
  @rake['cron'].invoke   
end

在此处替换您的 rake 任务名称,并请注意,如果您的加载路径中没有 lib 文件夹,您的要求可能是“lib/tasks/cron”。

我同意您应该只在 Rake 任务中做最少的工作,并将其余的工作推到模型中以便于测试。话虽如此,我认为在集成测试期间确保代码在我的 cron 任务中实际运行非常重要,因此我认为对 rake 任务进行非常温和的测试是合理的。

This works for me: (Rails3/ Ruby 1.9.2)

When /^the system does it's automated tasks$/ do    
  require "rake"
  @rake = Rake::Application.new
  Rake.application = @rake
  Rake.application.rake_require "tasks/cron"
  Rake::Task.define_task(:environment)
  @rake['cron'].invoke   
end

Substitute your rake task name here and also note that your require may be "lib/tasks/cron" if you don't have the lib folder in your load path.

I agree that you should only do minimal work in the Rake task and push the rest to models for ease of testing. That being said I think it's important to ensure that the code is ACTUALLY run in my cron tasks during my integration tests so I think very mild testing of the rake tasks is justified.

痴意少年 2024-09-04 13:01:13

由于测试耙子对我来说太多了,所以我倾向于解决这个问题。每当我发现自己要测试一个很长的 rake 任务时,我都会在 lib/ 中创建一个模块/类,并将任务中的所有代码移到那里。这将任务留给了一行 Ruby 代码,将其委托给更可测试的东西(类、模块,凡是你能想到的)。唯一尚未测试的是 rake 任务是否调用正确的代码行(并传递正确的参数),但我认为这是可以的。

告诉我们您的 spec_helper.rb 的第 21 行可能会很有用。但考虑到您发布的方法深入挖掘了 rake(指其实例变量),我将完全放弃它,因为我在上一段中建议了它。

Since testing rake is just too much for me, I tend to move this problem around. Whenever I find myself with a long rake task that I want to test, I create a module/class in lib/ and move all the code from the task there. This leaves the task to a single line of Ruby code, that delegates to something more testable (class, module, you name it). The only thing that remains untested is whether the rake task invokes the right line of code (and passes the right parameters), but I think that is OK.

It might be useful to tell us which is the 21nd line of your spec_helper.rb. But given that the approach you posted digs deep in rake (referring to its instance variables), I would entirely abandon it for what I suggested in the previous paragraph.

飘落散花 2024-09-04 13:01:13

我刚刚花了一点时间让黄瓜运行耙子任务,所以我想我应该分享我的方法。注意:这是使用 Ruby 2.0.0 和 Rake 10.0.4,但我认为自以前的版本以来该行为没有改变。

这有两个部分。第一个很简单:通过正确设置的 Rake::Application 实例,我们可以通过调用 #[] 访问其上的任务(例如 rake['数据:导入'])。一旦我们有了任务,我们就可以通过调用#invoke并传入参数来运行它(例如rake['data:import'].invoke('path/to/my/file. csv')

第二部分更尴尬:在完成 require 'rake' 后正确设置要使用的 Rake::Application 实例。 code> 我们可以访问 Rake 模块,它已经有一个应用程序实例,可以从 Rake.application 获取,但它尚未设置 - 它不知道。但是,假设我们使用了标准文件名之一:rakefileRakefile,它确实知道在哪里可以找到我们的 Rakefile。 >rakefile.rbRakefile.rb

要加载 rakefile,我们只需在应用程序上调用 #load_rakefile,但在此之前我们需要调用#handle_options。调用#handle_options会使用默认值填充options.rakelib。 > 未设置,那么 #load_rakefile 方法将会崩溃,因为它期望 options.rakelib 是可枚举的。

这是我最终得到的助手:

module RakeHelper
  def run_rake_task(task_name, *args)
    rake_application[task_name].invoke(*args)
  end

  def rake_application
    require 'rake'
    @rake_application ||= Rake.application.tap do |app|
      app.handle_options
      app.load_rakefile
    end
  end
end

World(RakeHelper)

将该代码弹出到 features/support/ 中的文件中,然后在您的步骤中使用 run_rake_task ,例如:

When /^I import data from a CSV$/ do
  run_rake_task 'data:import', 'path/to/my/file.csv'
end

I've just spent a little while getting cucumber to run a rake task so I thought I'd share my approach. Note: This is using Ruby 2.0.0 and Rake 10.0.4, but I don't think the behaviour has changed since previous versions.

There are two parts to this. The first is easy: with a properly set up instance of Rake::Application then we can access tasks on it by calling #[] (eg rake['data:import']). Once we have a task we can run it by calling #invoke and passing in the arguments (eg rake['data:import'].invoke('path/to/my/file.csv').

The second part is more awkward: properly setting up an instance of Rake::Application to work with. Once we've done require 'rake' we have access to the Rake module. It already has an application instance, available from Rake.application, but it's not yet set up — it doesn't know about any of our rake tasks. It does, however, know where to find our Rakefile, assuming we've used one of the standard file names: rakefile, Rakefile, rakefile.rb or Rakefile.rb.

To load the rakefile we just need to call #load_rakefile on the application, but before we can do that we need to call #handle_options. The call to #handle_options populates options.rakelib with a default value. If options.rakelib is not set then the #load_rakefile method will blow up, as it expects options.rakelib to be enumerable.

Here's the helper I've ended up with:

module RakeHelper
  def run_rake_task(task_name, *args)
    rake_application[task_name].invoke(*args)
  end

  def rake_application
    require 'rake'
    @rake_application ||= Rake.application.tap do |app|
      app.handle_options
      app.load_rakefile
    end
  end
end

World(RakeHelper)

Pop that code into a file in features/support/ and then just use run_rake_task in your steps, eg:

When /^I import data from a CSV$/ do
  run_rake_task 'data:import', 'path/to/my/file.csv'
end
你穿错了嫁妆 2024-09-04 13:01:13

自发布正确答案以来,行为可能已发生变化。我在执行需要运行相同 rake 任务的两个场景时遇到问题(尽管我使用 .execute 而不是 .invoke,但只有一个正在执行)。我想分享我解决这个问题的方法(Rails 4.2.5 和 Ruby 2.3.0)。

我用 @rake 标记了所有需要 rake 的场景,并且定义了一个钩子来仅设置 rake 一次。

# hooks.rb
Before('@rake') do |scenario|
  unless $rake
    require 'rake'
    Rake.application.rake_require "tasks/daily_digest"
    # and require other tasks
    Rake::Task.define_task(:environment)
    $rake = Rake::Task
  end
end

(这里建议使用全局变量: https: //github.com/cucumber/cucumber/wiki/Hooks#running-a-before-hook-only-once)

在步骤定义中,我简单地调用了 $rake

# step definition
Then(/^the daily digest task is run$/) do
  $rake['collector:daily_digest'].execute
end

欢迎任何反馈。

The behavior might have changed since the correct answer was posted. I was experiencing problems executing two scenarios that needed to run the same rake task (only one was being executed despite me using .execute instead of .invoke). I thought to share my approach to solve the issue (Rails 4.2.5 and Ruby 2.3.0).

I tagged all the scenarios that require rake with @rake and I defined a hook to setup rake only once.

# hooks.rb
Before('@rake') do |scenario|
  unless $rake
    require 'rake'
    Rake.application.rake_require "tasks/daily_digest"
    # and require other tasks
    Rake::Task.define_task(:environment)
    $rake = Rake::Task
  end
end

(Using a global variable is suggested here: https://github.com/cucumber/cucumber/wiki/Hooks#running-a-before-hook-only-once)

In the step definition I simply called $rake

# step definition
Then(/^the daily digest task is run$/) do
  $rake['collector:daily_digest'].execute
end

Any feedback is welcome.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文