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

gre*_*tes 6 rake rspec ruby-on-rails

我正在尝试在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
Run Code Online (Sandbox Code Playgroud)

规格/ 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
Run Code Online (Sandbox Code Playgroud)

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)>'
Run Code Online (Sandbox Code Playgroud)

Dav*_*ton 7

果壳:将你before改为before :all(而不是:each).

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

Rake.application.rake_require 'lib/tasks/demo_tasks', 
                              [Rails.root.to_s], 
                              []
Run Code Online (Sandbox Code Playgroud)

细节

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

$"是一个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
Run Code Online (Sandbox Code Playgroud)

您可以在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
Run Code Online (Sandbox Code Playgroud)

IMO,由于多种原因而不太理想.这是我同意的总结.


And*_*son 5

#invoke一个流行的搜索引擎引导我来到这里,就像在我的例子中,当对给定测试多次使用时,我看到测试失败。下面的解决方案建立在@dave-newtown\'s 答案的基础上。

\n\n

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

\n\n
RSpec.describe "demo:test" do\n  it "runs" do\n    expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked)\n    Rake::Task["demo:test"].invoke\n  end\n\n  it "runs" do\n    expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked)\n    Rake::Task["demo:test"].invoke\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

...it如果测试写得好并且任务调用正确,则可能会通过第一个运行的任务,但it在给定的范围内,第二个任务总是会失败Rake.application,使用#invoke\xc2\xa0only 只运行一次任务。has-before-run-before 状态显然被记住在Rake.application实例内部。

\n\n

是的,这确实意味着至少在 Rake v12测试下,许多展示如何测试 Rake 任务的在线文章都是不正确的,或者侥幸逃脱,因为它们只在示例中显示了针对任何给定任务的单个测试。

\n\n

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

\n\n

相反,将已接受的答案与网上的其他零碎内容混合起来会产生这种替代方案:

\n\n
require \'spec_helper\'\nrequire \'rake\'\n\nRSpec.describe \'demo:test\' do\n  before :each do\n    Rake.application = Rake::Application.new\n    Rake.application.rake_require \'lib/tasks/demo_tasks\', [Rails.root.to_s], []\n    Rake::Task.define_task(:environment)\n  end\n\n  it \'runs\' do\n    expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked)\n    Rake.application.invoke_task(\'demo.test\')\n  end\n\n  it \'runs with a parameter\' do\n    expect(SomethingWeAreInvoking).to eql(ProofIfWasInvoked)\n    Rake.application.invoke_task(\'demo.test[42]\')\n  end\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 它准备 Rake on before :each
  • \n
  • 每次Rake.application都会创建一个新的;这意味着我们可以invoke在任意数量的测试中使用,尽管在任何单个测试中只能使用一次。
  • \n
  • 如果我们只使用Rake.application开箱即用的实例,我们可以在Rake.application.rake_require \'tasks/demo_tasks\'设置所有路径等时进行编写,但由于我们肯定需要为每个测试一个新的 Rake 应用程序实例,以避免跨测试“弄脏”其状态,需要 @dave-newtown 的“普通”表格。
  • \n
  • 我用Rake.application.invoke_task而不是Rake::Task[...].invoke. 这使参数的语法与 Rake 命令行上使用的语法相同,我认为这是测试采用参数的任务的更“准确”和自然的方法。
  • \n
\n\n

是的,这确实意味着至少在经过测试的 Rake v12 下,许多展示如何测试 Rake 任务的在线文章都是不正确的,或者侥幸逃脱,因为它们只在示例中显示了针对任何给定任务的单个测试。早期的 Rake 版本很可能没有这种行为,因此这些文章在撰写时是正确的。

\n\n

希望有人觉得这有帮助。

\n\n
\n

参考文章:

\n\n \n\n

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

\n
\n