'rake task being invoked multiple times

I have a strange problem with testing my rails tasks. When I'm running all the tests for my rake tasks they are being invoked multiple times, but when I run only one test file everything is suddenly working. Here is an example code:

rake task

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 test

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

in the console output i can see something like this:

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

so the task was invoked two times.

I have no idea why this is happening and I'm 100% sure I'm not invoking it in my code twice. I'm suspecting that either it is some rake configuration for rspec that I'm missing or the gem for rake tasks that I'm using - after_party.

I'm having hard times debugging it, tried using show-stack with binding.pry while I'm in the rake task that is causing the issues but didn't manage to see at what point additional rake task was being fired.

anyone has seen such issue before?

EDIT: stack trace of running show-stack command inside it block, just before the invoke rake task:

=> #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> 

also attaching stack trace from inside the rake task itself:

=> #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> 

EDIT 2: added more info

I've checked what would happen if I place binding.pry like that (and inside the rake task as well)

  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

below after party rake task code

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

and the output was that code execution stopped at no1, then we went into the rake task and stopped at no2, afterwards the task was ran AGAIN (so again stopped at no2) and afterwards it was stopping at no3



Solution 1:[1]

It depends how you load your rake task in the tests

# 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

Is not so good in one single test run of this two files. Rake seems to register the rake tasks twice. You need to take care that the rake task just get registered once like:

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

This should work because the test registration is only done once. Or use somehow require. require checks if a file is already registered.

Solution 2:[2]

I had the same problem, but resolved it by checking how the tasks were loaded in apparently unrelated tasks spec files. In another spec file, the tested task over there was loaded as

Rails.application.load_tasks

which actually loads all tasks. So in the spec file I was working on, I had of course to require the task I wanted to test; so that would load this task a second time only when I would load the whole test suite.

Replacing this line with

Rake.application.rake_require 'tasks/your_task_file'

and changing the task call with .execute instead of .invoke did the trick. .execute does not load the :environment though, so if you need it you might want to find a way to do so

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 PeterNL
Solution 2 Tim