0
votes

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

1
Can you share both stack traces, when you pause at the line puts "JUST BEFORE INVOKING THE TASK: " twice?Grzegorz
@Grzegorz added the stack trace - first one is from inside the it statement. Second is from inside the rake task itself (every time the rake task is being invoked the stack trace appears the same)beniutek
I'm not sure we're on the same page. There's Running deploy task 'create_users_on_another_app' message showing in the console. Set up a breakpoint there (you should pause there twice), and let's compare stack traces from both of those pauses.Grzegorz
when I set breaking point inside it statement it only gets called once (the result is the first stacktrace). When I set the breaking point inside the rake task itself it gets called twice (that's the second stacktrace, when I'm checking this stacktrace in to second invokation of rake task it is the same as in the first one).beniutek
"(that's the second stacktrace, when I'm checking this stacktrace in to second invokation of rake task it is the same as in the first one)." This is extremely hard to believe. Are you 100% sure? As in: I saved both stack traces, and diffed them and there was no difference whatsoever? They have to have different paths leading to "#13 [method] invoke <Rake::Task#invoke(*args)>"Grzegorz

1 Answers

0
votes

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.