[英]Running a Rake task with parameters
I have been working to get test coverage on the following rake task with the attached spec. 我一直在努力通过随附的规格获得有关以下rake任务的测试报道。 However, nothing I appear to try sends the
env
parameter through correctly? 但是,我似乎无法尝试正确发送
env
参数吗?
Test Failures 测试失败
1) myapp:database tasks myapp:database :recreate works
Failure/Error: system("RAILS_ENV=#{args[:env]} rake db:create")
main received :system with unexpected arguments
expected: (/RAILS_ENV=testing rake db:drop/)
got: ("RAILS_ENV=testing rake db:create")
Diff:
@@ -1,2 +1,2 @@
-[/RAILS_ENV=testing rake db:drop/]
+["RAILS_ENV=testing rake db:create"]
# ./lib/tasks/database.rake:9:in `block (3 levels) in <top (required)>'
# ./spec/lib/tasks/database_rake_spec.rb:17:in `block (5 levels) in <top (required)>'
# ./spec/lib/tasks/database_rake_spec.rb:17:in `block (4 levels) in <top (required)>'
# -e:1:in `<main>'
Spec 规格
describe 'myapp:database tasks' do
include_context 'rake'
let(:task_paths) { ['tasks/database', 'tasks/seed'] }
# rubocop:disable RSpec/MultipleExpectations
describe 'myapp:database' do
before do
invoke_task.reenable
end
# TODO!
context ':recreate', focus: true do
let(:task_name) { 'myapp:database:recreate' }
it 'works' do
expect_any_instance_of(Object).to receive(:system).with(/RAILS_ENV=testing rake db:drop/).and_return(true)
expect { invoke_task.invoke('testing') }.to output(
"\nDropping the testing database\n"\
"\nCreating the testing database\n"\
"\nRunning the testing database migrations\n"
).to_stdout
end
end
# rubocop:disable RSpec/MessageSpies
context ':reset' do
let(:task_name) { 'myapp:database:reset' }
it 'works' do
expect(Rake::Task['myapp:database:recreate']).to receive(:invoke).twice
expect(Rake::Task['myapp:seed:all']).to receive(:invoke)
expect { invoke_task.invoke }.to output("\nResetting the development and testing databases\n").to_stdout
end
end
end
# rubocop:enable all
end
Task 任务
namespace :myapp do
namespace :database do
if Rails.env.development? || Rails.env.test?
desc 'Drop and create a database, ["env"] = environment'
task :recreate, [:env] => [:environment] do |_t, args|
puts "\nDropping the #{args[:env]} database\n"
system("RAILS_ENV=#{args[:env]} rake db:drop")
puts "\nCreating the #{args[:env]} database\n"
system("RAILS_ENV=#{args[:env]} rake db:create")
puts "\nRunning the #{args[:env]} database migrations\n"
system("RAILS_ENV=#{args[:env]} rake db:migrate")
end
desc 'Reset the db data and setup development'
task reset: :environment do
puts "\nResetting the development and testing databases\n"
%w(development test).each do |db|
Rake::Task['myapp:database:recreate'].invoke(db)
end
Rake::Task['myapp:seed:all'].invoke
end
end
end
end
Shared Context 共享上下文
shared_context 'rake' do
let(:invoke_task) { Rake.application[task_name] }
let(:highline) { instance_double(HighLine) }
before do
task_paths.each do |task_path|
Rake.application.rake_require(task_path)
end
Rake::Task.define_task(:environment)
end
before do
allow(HighLine).to receive(:new).and_return(highline)
# rubocop:disable all
allow_any_instance_of(Object).to receive(:msg).and_return(true)
allow_any_instance_of(Object).to receive(:error_msg).and_return(true)
# rubocop:enable all
end
end
Update 更新资料
context ':recreate' do
let(:task_name) { 'myapp:database:recreate' }
it 'works' do
expect_any_instance_of(Object).to receive(:system).with(/RAILS_ENV=testing rake db:drop/).and_return(true)
expect_any_instance_of(Object).to receive(:system).with(/RAILS_ENV=testing rake db:create/).and_return(true)
expect_any_instance_of(Object).to receive(:system).with(/RAILS_ENV=testing rake db:migrate/).and_return(true)
expect { invoke_task.invoke('testing') }.to output(
"\nDropping the testing database\n"\
"\nCreating the testing database\n"\
"\nRunning the testing database migrations\n"
).to_stdout
end
end
As I mentioned in a comment, the task isn't being invoked from the test because of the way you're stubbing here: 正如我在评论中提到的,由于您在此处进行存根的方式,因此未从测试中调用任务:
expect(Rake::Task['myapp:seed:all']).to receive(:invoke)
Although this checks whether invoke
was called, it doesn't actually invoke invoke
(actually, it makes the method return nil). 尽管此检查是否
invoke
,但实际上并没有调用invoke
(实际上,它使方法返回nil)。 To change that, you can either: 要更改此设置,您可以:
and_return(<something>)
and_return(<something>)
and_call_original
. and_call_original
。 Probably in this case you'd want to use and_call_original
since you want to investigate what actually happens in the task. 在这种情况下,可能要使用
and_call_original
因为您想调查任务中实际发生的情况。 In order to stub individual method calls in the task, the approach you have been using ( expect_any_instance_of(Object).to receive(:system)
) will technically work, but could probably be refactored to be more decoupled from the code. 为了在任务中存根单个方法调用,您一直使用的方法(
expect_any_instance_of(Object).to receive(:system)
)从技术上讲是可行的,但可以将其重构为与代码更分离。
For example, you could separate each system
call into its own method (available to the rake task), and then call those from the test. 例如,您可以将每个
system
调用分成其自己的方法(可用于rake任务),然后从测试中调用它们。 Then in order to stub it you only need to pass the method name. 然后,为了存根,您只需要传递方法名称即可。 If you want, you can then go and unit test each of those methods individually, putting the
system
call expectation in there. 如果需要,可以分别对每个方法进行单元测试,并将
system
调用期望值放在其中。
I don't recall where exactly but I've heard it advised to not do any acual programming in Rake tasks. 我不记得确切在哪里,但我听说它建议不要在Rake任务中进行任何常规编程。 Put your code somewhere in your regular codebase, and call those methods from the rake task.
将您的代码放在常规代码库中的某个位置,然后从rake任务中调用这些方法。 This can be seen as an example of a more general pattern which is to refactor large methods into smaller ones.
这可以看作是更通用模式的示例,该模式将大型方法重构为较小的方法。 Writing code this way (and also with a functional style, but I won't get into that) makes your life easier when testing.
这样编写代码(以及功能样式,但我不会讲),使您的测试工作变得更轻松。
onto your followup question: 您的后续问题:
as you can see in the test case's failure message, the only difference between the actual and expected is that one is a regex and the other is a string. 从测试用例的失败消息中可以看到,实际和预期的唯一区别是一个是正则表达式,另一个是字符串。
A simple fix for this is to change this line: 一个简单的解决方法是更改此行:
expect_any_instance_of(Object).to receive(:system).with(/RAILS_ENV=testing rake db:drop/).and_return(true)
so that the with()
argument is a string, not a regex 这样
with()
参数是一个字符串,而不是正则表达式
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.