我试图在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)>'
答案 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版本很可能不会以这种方式运行,因此这些文章在撰写时是正确的。
希望有人觉得这很有帮助。
参考文章:
- https://www.rubydoc.info/gems/rake/Rake/Task#invoke-instance_method-线索在'调用任务(如果需要)中。先调用先决条件”(我的重点)
- 比较https://www.rubydoc.info/gems/rake/Rake/Task#execute-instance_method-“执行与此任务相关的操作”,没有提及“如果需要”或前提条件
- 搜索引擎生成诸如https://medium.com/@sampatbadhe/rake-task-invoke-or-execute-419cd689c3bd
之类的文章(搜索引擎提示:测试rake rspec测试调用仅被调用一次运行)