【问题标题】:rake task being invoked multiple timesrake 任务被多次调用
【发布时间】:2020-04-28 18:18:10
【问题描述】:

我在测试我的 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

我很难调试它,尝试使用 show-stackbinding.pry,而我正在执行导致问题的 rake 任务,但没有看到在什么时候触发了额外的 rake 任务.

有人见过这样的问题吗?

编辑:在 it 块内运行 show-stack 命令的堆栈跟踪,就在 invoke 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 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

【问题讨论】:

  • 当您在puts "JUST BEFORE INVOKING THE TASK: " 行停顿两次时,您可以共享两个堆栈跟踪吗?
  • @Grzegorz 添加了堆栈跟踪 - 第一个来自 it 语句内部。其次是从 rake 任务本身内部(每次调用 rake 任务时,堆栈跟踪都会显示相同)
  • 我不确定我们是否在同一页面上。控制台中显示Running deploy task 'create_users_on_another_app' 消息。在那里设置一个断点(你应该在那里暂停两次),让我们比较这两个暂停的堆栈跟踪。
  • 当我在it 语句中设置断点时,它只被调用一次(结果是第一个堆栈跟踪)。当我在 rake 任务本身中设置断点时,它会被调用两次(这是第二个堆栈跟踪,当我将此堆栈跟踪检查到第二次调用 rake 任务时,它与第一个相同)。
  • "(这是第二个堆栈跟踪,当我将此堆栈跟踪检查到第二次调用 rake 任务时,它与第一个相同)。"这是非常难以相信的。你100%确定吗?如:我保存了两个堆栈跟踪,并对它们进行了比较,没有任何区别?他们必须有不同的路径导致“#13 [method] invoke <:task>”

标签: ruby-on-rails rspec rake rspec-rails


【解决方案1】:

这取决于你如何在测试中加载你的 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

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

【讨论】:

  • 我无权访问该特定代码库(也无法确认这是否会解决问题),但这是一个宝贵的见解,谢谢!
【解决方案2】:

我遇到了同样的问题,但通过检查任务是如何加载到明显不相关的任务规范文件中的方式解决了这个问题。 在另一个规范文件中,那里的测试任务被加载为

Rails.application.load_tasks

它实际上加载了所有任务。 所以在我正在处理的规范文件中,我当然需要要求我想要测试的任务;这样只有在我加载整个测试套件时才会再次加载此任务。

将这一行替换为

Rake.application.rake_require 'tasks/your_task_file'

并使用.execute 而不是.invoke 更改任务调用就成功了。 .execute 不会加载 :environment,所以如果你需要它,你可能想找到一种方法来这样做

【讨论】:

    猜你喜欢
    • 2014-04-19
    • 2023-03-13
    • 1970-01-01
    • 2013-03-27
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    • 2019-12-22
    • 1970-01-01
    相关资源
    最近更新 更多