(显然)两个耙子任务的相同测试;只有一个通过

时间:2012-09-08 22:58:55

标签: ruby-on-rails rspec rake

我试图在rspec中为两个rake任务编写测试,这两个rake任务在同一个文件中定义(在Rails 3.0.11项目中)。出于某种原因,只有其中一个通过。我已经编写了一个小型演示来抽象出任务的实际内容,同样的事情发生了。从命令行使用rake调用时,这两个任务都有效。发生了什么事?这是我的演示:

LIB /任务/ demo_tasks.rake

namespace :demo do
  task :test => :environment do
    puts "test!"
  end

  task :test_two => :environment do
    puts "second test!"
  end
end

规格/ LIB /任务/ demo_spec.rb

require 'spec_helper'
require 'rake'

describe "test tasks" do
  let(:rake) do
    app = Rake::Application.new
    app.options.silent = true
    app
  end

  before :each do
    Rake.application = rake
    Rake.application.rake_require 'lib/tasks/demo_tasks',
                                  [Rails.root.to_s]
    Rake::Task.define_task :environment
  end

  describe "demo:test" do
    it "runs" do
      rake["demo:test"].invoke
    end
  end

  describe "demo:test_two" do
    it "also_runs" do
      rake["demo:test_two"].invoke
    end
  end
end

rspec spec / lib / tasks / demo_spec.rb

test tasks
  demo:test
test!
    runs
  demo:test_two
    also_runs (FAILED - 1)

Failures:

  1) test tasks demo:test_two also_runs
     Failure/Error: rake["demo:test_two"].invoke
     RuntimeError:
       Don't know how to build task 'demo:test_two'
     # ./spec/lib/tasks/demo_spec.rb:26:in `block (3 levels) in <top (required)>'

2 个答案:

答案 0 :(得分:6)

Nutshell :将您的before更改为before :all(而不是:each)。

或:将空数组作为第三个参数传递给rake_require

Rake.application.rake_require 'lib/tasks/demo_tasks', 
                              [Rails.root.to_s], 
                              []

<强>详情

def rake_require(file_name, paths=$LOAD_PATH, loaded=$")
  fn = file_name + ".rake"
  return false if loaded.include?(fn)
  ...

$"是一个Ruby特殊变量,它包含由require加载的模块数组。

如果未传递可选参数,rake_require将使用Ruby加载的模块数组。这意味着模块不会再次加载:Ruby知道模块已加载,rake检查以查看Ruby知道的内容,并且它是每个测试的新rake实例。

切换到before :all因为它意味着let块仅运行一次:一个rake实例,一个模块加载,每个人都很高兴。

所有这些都说,为什么要重新加载耙环境两次呢?您的目标是测试您的任务,这不需要为每个规范提供新的rake上下文。

你可以在每个规范中以一些轻微的冗长为代价完全消除本地:

describe "test tasks" do
  before :all do
    Rake.application = Rake::Application.new
    Rake.application.rake_require 'lib/tasks/demo_tasks', [Rails.root.to_s]
    Rake::Task.define_task :environment
  end

  describe "demo:test" do
    it "runs" do
      Rake::Task["demo:test"].invoke
    end
  end
end

您可以在before块中定义一个实例变量,以避免Rake::Task引用:

before :all do
  @rake = Rake::Application.new
  Rake.application = @rake
  Rake.application.rake_require 'lib/tasks/demo_tasks', [Rails.root.to_s]
  Rake::Task.define_task :environment
end

describe "demo:test" do
  it "runs" do
    @rake["demo:test"].invoke

IMO,由于多种原因不太理想。 Here's a summary I agree with

答案 1 :(得分:1)

一个流行的搜索引擎将我引到了这里,就我而言,我发现给定测试多次使用#invoke时测试失败。下面的解决方案基于@ dave-newtown的答案。

之所以出现此问题,是因为在撰写本文时(Rake v12),#invoke仅运行一次任务 ,例如:

RSpec.describe "demo:test" do
  it "runs" do
    expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked)
    Rake::Task["demo:test"].invoke
  end

  it "runs" do
    expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked)
    Rake::Task["demo:test"].invoke
  end
end

...如果测试编写得当且任务正确调用,则可能首先进行it的运行才能通过,但是在给定的{{1}内,第二次it总是失败的},Rake.application的使用只运行一次任务。显然在#invoke实例中可以记住has-been-run-before状态。

是的,这确实意味着至少在经过测试的Rake v12 下,很多在线文章展示了如何测试Rake任务是不正确的,或者因为它只显示了而放弃了它。在示例中针对任何给定任务进行一次测试。

我们可以使用Rake的Rake.application,但这不会运行依赖的任务,因此会导致其自身的一系列问题,并使我们远离测试Rake堆栈,就好像在命令行上调用它一样。 / p>

取而代之的是,将接受的答案与在线上的其他点点滴滴混合起来,就会产生这种选择:

#execute
  • 它准备require 'spec_helper' require 'rake' RSpec.describe 'demo:test' do before :each do Rake.application = Rake::Application.new Rake.application.rake_require 'lib/tasks/demo_tasks', [Rails.root.to_s], [] Rake::Task.define_task(:environment) end it 'runs' do expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked) Rake.application.invoke_task('demo.test') end it 'runs with a parameter' do expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked) Rake.application.invoke_task('demo.test[42]') end end 上的Rake。
  • 每次创建一个新的before :each;这意味着我们可以在任何数量的测试中使用Rake.application,尽管在任何单个测试中只能使用一次。
  • 如果我们仅使用现成的invoke实例,就可以在设置所有路径等的情况下编写Rake.application,但是由于我们确实需要一个新的Rake应用程序为了避免每个测试的实例在整个测试中“弄脏”其状态,需要使用@ dave-newtown的“ longhand”形式。
  • 我使用Rake.application.rake_require 'tasks/demo_tasks'而不是Rake.application.invoke_task。这使参数的语法与Rake命令行中使用的参数处于同等状态,我认为这是测试带有args的任务的更“准确”且自然的方法。

是的,这确实意味着至少在经过测试的Rake v12 下,很多在线文章显示了如何测试Rake任务不正确,或者因为仅显示一个测试而放弃了它。对于他们示例中的任何给定任务。早期的Rake版本很可能不会以这种方式运行,因此这些文章在撰写时是正确的。

希望有人觉得这很有帮助。

  

参考文章:

           

(搜索引擎提示:测试rake rspec测试调用仅被调用一次运行)

相关问题