首页 > 解决方案 > rake 任务被多次调用

问题描述

我在测试我的 Rails 任务时遇到了一个奇怪的问题。当我为我的 rake 任务运行所有测试时,它们会被多次调用,但是当我只运行一个测试文件时,一切都会突然工作。这是一个示例代码:

耙任务

namespace :after_party do
  desc 'Deployment task: create_users_on_another_app'
  task create_users_on_another_app: :environment do
      ...some code
      AppClient.post(some_url, some_payload, some_header)
      ...some more code
      AfterParty::TaskRecord.create version: '20190620160110'
    end
  end
end

rspec 测试

describe 'create_users_on_another_app' do
  before do
    allow(AppClient).to receive(:post).and_return(some_response)
  end

  it "creates users" do
    expect(AppClient).to receive(:post).with(some_url, some_data, some_header)
    puts "JUST BEFORE INVOKING THE TASK: "
    Rake::Task['after_party:create_users_on_another_app'].invoke

    puts "DONE"
  end
end

在控制台输出中,我可以看到如下内容:

> JUST BEFORE INVOKING THE TASK: 
> Running deploy task 'create_users_on_another_app'
> Running deploy task 'create_users_on_another_app'
> DONE

所以任务被调用了两次。

我不知道为什么会发生这种情况,而且我 100% 确定我没有在我的代码中调用它两次。我怀疑要么是我缺少的 rspec 的一些 rake 配置,要么是我正在使用的 rake 任务的 gem - after_party

我很难调试它,在我处于导致问题的 rake 任务时尝试使用show-stackwith binding.pry,但没有设法看到在什么时候触发了额外的 rake 任务。

有人见过这样的问题吗?

编辑:在rake 任务it之前,在块内运行 show-stack 命令的堆栈跟踪:invoke

=> #0  <main> 
   #1 [block]   block in run <Byebug::PryProcessor#run(&_block)>
   #2 [method]  run <Byebug::PryProcessor#run(&_block)>
   #3 [method]  resume_pry <Byebug::PryProcessor#resume_pry()>
   #4 [method]  at_line <Byebug::PryProcessor#at_line()>
   #5 [method]  at_line <Byebug::Context#at_line()>
   #6 [block]   block (2 levels) in <top (required)> 
   #7 [block]   block in run <RSpec::Core::Example#run(example_group_instance, reporter)>
   #8 [block]   block in with_around_and_singleton_context_hooks <RSpec::Core::Example#with_around_and_singleton_context_hooks()>
   #9 [block]   block in with_around_example_hooks <RSpec::Core::Example#with_around_example_hooks()>
   #10 [block]   block in run <RSpec::Core::Hooks::HookCollections#run(position, scope, example_or_group)>
   #11 [block]   block in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #12 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #13 [block]   block (2 levels) in <module:MinitestLifecycleAdapter> 
   #14 [method]  instance_exec <RSpec::Core::Example#instance_exec(*args, &block)>
   #15 [method]  execute_with <RSpec::Core::Hooks::AroundHook#execute_with(example, procsy)>
   #16 [block]   block (2 levels) in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #17 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #18 [block]   block (3 levels) in <top (required)> 
   #19 [method]  perform_enqueued_jobs <ActiveJob::TestHelper#perform_enqueued_jobs(?, ?)>
   #20 [block]   block (2 levels) in <top (required)> 
   #21 [method]  instance_exec <RSpec::Core::Example#instance_exec(*args, &block)>
   #22 [method]  execute_with <RSpec::Core::Hooks::AroundHook#execute_with(example, procsy)>
   #23 [block]   block (2 levels) in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #24 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #25 [block]   block in run <RSpec::Retry#run()>
   #26 [method]  run <RSpec::Retry#run()>
   #27 [method]  run_with_retry <RSpec::Core::Example::Procsy#run_with_retry(opts=?)>
   #28 [block]   block (2 levels) in setup <self.setup(UNKNOWN) (undefined method)>
   #29 [method]  instance_exec <RSpec::Core::Example#instance_exec(*args, &block)>
   #30 [method]  execute_with <RSpec::Core::Hooks::AroundHook#execute_with(example, procsy)>
   #31 [block]   block (2 levels) in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #32 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #33 [method]  run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #34 [method]  run <RSpec::Core::Hooks::HookCollections#run(position, scope, example_or_group)>
   #35 [method]  with_around_example_hooks <RSpec::Core::Example#with_around_example_hooks()>
   #36 [method]  with_around_and_singleton_context_hooks <RSpec::Core::Example#with_around_and_singleton_context_hooks()>
   #37 [method]  run <RSpec::Core::Example#run(example_group_instance, reporter)>
   #38 [block]   block in run_examples <RSpec::Core::ExampleGroup.run_examples(reporter)>
   #39 [method]  run_examples <RSpec::Core::ExampleGroup.run_examples(reporter)>
   #40 [method]  run <RSpec::Core::ExampleGroup.run(reporter=?)>
   #41 [block]   block (3 levels) in run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #42 [block]   block (2 levels) in run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #43 [method]  with_suite_hooks <RSpec::Core::Configuration#with_suite_hooks()>
   #44 [block]   block in run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #45 [method]  report <RSpec::Core::Reporter#report(expected_example_count)>
   #46 [method]  run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #47 [method]  run <RSpec::Core::Runner#run(err, out)>
   #48 [method]  run <RSpec::Core::Runner.run(args, err=?, out=?)>
   #49 [method]  invoke <RSpec::Core::Runner.invoke()>
   #50 [top]     <top (required)> 
   #51 [eval]    <main> 
   #52 [main]    <main> 

还从 rake 任务本身内部附加堆栈跟踪:

=> #0  <main> 
   #1 [block]   block in run <Byebug::PryProcessor#run(&_block)>
   #2 [method]  run <Byebug::PryProcessor#run(&_block)>
   #3 [method]  resume_pry <Byebug::PryProcessor#resume_pry()>
   #4 [method]  at_line <Byebug::PryProcessor#at_line()>
   #5 [method]  at_line <Byebug::Context#at_line()>
   #6 [block]   block (2 levels) in <top (required)> 
   #7 [block]   block in execute <Rake::Task#execute_without_bugsnag(args=?)>
   #8 [method]  execute <Rake::Task#execute_without_bugsnag(args=?)>
   #9 [method]  execute_with_bugsnag <Rake::Task#execute_with_bugsnag(args=?)>
   #10 [block]   block in invoke_with_call_chain <Rake::Task#invoke_with_call_chain(task_args, invocation_chain)>
   #11 [method]  mon_synchronize <MonitorMixin#mon_synchronize()>
   #12 [method]  invoke_with_call_chain <Rake::Task#invoke_with_call_chain(task_args, invocation_chain)>
   #13 [method]  invoke <Rake::Task#invoke(*args)>
   #14 [block]   block (2 levels) in <top (required)> 
   #15 [block]   block in run <RSpec::Core::Example#run(example_group_instance, reporter)>
   #16 [block]   block in with_around_and_singleton_context_hooks <RSpec::Core::Example#with_around_and_singleton_context_hooks()>
   #17 [block]   block in with_around_example_hooks <RSpec::Core::Example#with_around_example_hooks()>
   #18 [block]   block in run <RSpec::Core::Hooks::HookCollections#run(position, scope, example_or_group)>
   #19 [block]   block in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #20 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #21 [block]   block (2 levels) in <module:MinitestLifecycleAdapter> 
   #22 [method]  instance_exec <RSpec::Core::Example#instance_exec(*args, &block)>
   #23 [method]  execute_with <RSpec::Core::Hooks::AroundHook#execute_with(example, procsy)>
   #24 [block]   block (2 levels) in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #25 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #26 [block]   block (3 levels) in <top (required)> 
   #27 [method]  perform_enqueued_jobs <ActiveJob::TestHelper#perform_enqueued_jobs(?, ?)>
   #28 [block]   block (2 levels) in <top (required)> 
   #29 [method]  instance_exec <RSpec::Core::Example#instance_exec(*args, &block)>
   #30 [method]  execute_with <RSpec::Core::Hooks::AroundHook#execute_with(example, procsy)>
   #31 [block]   block (2 levels) in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #32 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #33 [block]   block in run <RSpec::Retry#run()>
   #34 [method]  run <RSpec::Retry#run()>
   #35 [method]  run_with_retry <RSpec::Core::Example::Procsy#run_with_retry(opts=?)>
   #36 [block]   block (2 levels) in setup <self.setup(UNKNOWN) (undefined method)>
   #37 [method]  instance_exec <RSpec::Core::Example#instance_exec(*args, &block)>
   #38 [method]  execute_with <RSpec::Core::Hooks::AroundHook#execute_with(example, procsy)>
   #39 [block]   block (2 levels) in run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #40 [method]  call <RSpec::Core::Example::Procsy#call(*args, &block)>
   #41 [method]  run_around_example_hooks_for <RSpec::Core::Hooks::HookCollections#run_around_example_hooks_for(example)>
   #42 [method]  run <RSpec::Core::Hooks::HookCollections#run(position, scope, example_or_group)>
   #43 [method]  with_around_example_hooks <RSpec::Core::Example#with_around_example_hooks()>
   #44 [method]  with_around_and_singleton_context_hooks <RSpec::Core::Example#with_around_and_singleton_context_hooks()>
   #45 [method]  run <RSpec::Core::Example#run(example_group_instance, reporter)>
   #46 [block]   block in run_examples <RSpec::Core::ExampleGroup.run_examples(reporter)>
   #47 [method]  run_examples <RSpec::Core::ExampleGroup.run_examples(reporter)>
   #48 [method]  run <RSpec::Core::ExampleGroup.run(reporter=?)>
   #49 [block]   block (3 levels) in run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #50 [block]   block (2 levels) in run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #51 [method]  with_suite_hooks <RSpec::Core::Configuration#with_suite_hooks()>
   #52 [block]   block in run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #53 [method]  report <RSpec::Core::Reporter#report(expected_example_count)>
   #54 [method]  run_specs <RSpec::Core::Runner#run_specs(example_groups)>
   #55 [method]  run <RSpec::Core::Runner#run(err, out)>
   #56 [method]  run <RSpec::Core::Runner.run(args, err=?, out=?)>
   #57 [method]  invoke <RSpec::Core::Runner.invoke()>
   #58 [top]     <top (required)> 
   #59 [eval]    <main> 
   #60 [main]    <main> 

编辑 2:添加了更多信息

我已经检查过如果我这样放置会发生什么binding.pry(以及在 rake 任务中)

  it "creates users" do
    expect(AppClient).to receive(:post).with(some_url, some_data, some_header)
    binding.pry # no1 before rake task invokation
    Rake::Task['after_party:create_users_on_another_app'].invoke
    binding.pry # no3 after rake task invokation
    puts "DONE"
  end

下面是派对耙任务代码

namespace :after_party do
  desc 'Deployment task: create_users_on_another_app'
  task create_users_on_another_app: :environment do
      ...some code
      binding.pry # no2 inside the task
      AppClient.post(some_url, some_payload, some_header)
      ...some more code
      AfterParty::TaskRecord.create version: '20190620160110'
    end
  end
end

并且输出是代码执行停止在no1,然后我们进入 rake 任务并停止在no2,之后任务再次运行(所以再次停止在no2),然后它停止在no3

标签: ruby-on-railsrspecrakerspec-rails

解决方案


这取决于你如何在测试中加载你的 rake 任务

# File1
dscribe 'some test 1' do 
  before do
   Dir.glob('lib/tasks/*.rake').each { |r| load r }
  end
  ...
end

# File2
dscribe 'some test 2' do 
  before do
   Dir.glob('lib/tasks/*.rake').each { |r| load r }
  end
  ...
end

在这两个文件的一次测试运行中不太好。Rake 似乎注册了两次 rake 任务。您需要注意 rake 任务只需注册一次,如下所示:

RSpec.configure do |config|
   confige.before :suite do
     Dir.glob('lib/tasks/*.rake').each { |r| load r }
   end
end

这应该有效,因为测试注册只进行一次。或以某种方式使用require. require检查文件是否已注册。


推荐阅读