简体   繁体   English

使用参数运行Rake任务

[英]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: 要更改此设置,您可以:

  1. tack on an and_return(<something>) and_return(<something>)
  2. tack on 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM