testing rake tasks with Rspec is not accepting arguments
So, according to this and this, the following are some ways of calling rake tasks with arguments:
Rake.application.invoke_task("my_task[arguments]")
or
Rake::Task["my_task"].invoke(arguments)
On the other hand, I was calling the task as:
Rake::Task["my_task[arguments]"].invoke
Which was a Mis combination of the above two methods.
A big thank you
to Jason for his contribution and suggestion.
Rspec testing rake tasks
It turns out that when a rake task has been invoke
d it won't be ran again. You have to either reenable
the task after invocation or use execute
How to invoke RSpec inside a rake task?
To run RSpec through its rake integration, you need to both define a task and invoke it:
# all_tests.rake
require 'rspec/core/rake_task'
# Define the "spec" task, at task load time rather than inside another task
RSpec::Core::RakeTask.new(:spec)
desc 'Run all tests, even those usually excluded.'
task all_tests: :environment do
ENV['RUN_ALL_TESTS'] = 'true'
Rake::Task['spec'].invoke
end
Rake::Task['spec'].invoke
did nothing when you tried it because rake turns a task name which is not a name of a defined task but is a file name into a Rake::FileTask
, both on the command line and in Rake::Task
. You had no 'spec'
task defined, but you have a spec
directory, so rake spec
ran without error and did nothing.
Running Rake tasks in Rspec Tests
You can invoke Rake tasks as following:
require 'rake'
Rake::Task[name].invoke
In this case this would result in the following code:
require 'rake'
Rake::Task['db:test:purge'].invoke
Testing Rake in Rails: Multiple Error Raises Silenced In Test
I can think about two solution of your problem.
But first we need to find out where is the root of the problem.
Root of the problem
Let's start with a line from your code:
Rake::Task[task].enhance ['guard_dangerous_tasks']
Comparing it with source code of Rake::Task
# File rake/task.rb, line 96
def enhance(deps=nil, &block)
@prerequisites |= deps if deps
@actions << block if block_given?
self
end
you can see, that guard_dangerous_tasks
should be added to @prerequisites
array. It can be easily checked:
p Rake::Task['db:reset'].prerequisites # => ["environment", "load_config", "guard_dangerous_tasks"]
Continuing with you source code.
You use invoke
to execute tasks. If we pay close attention to invoke
's' documentation, it states:
Invoke the task if it is needed.
Once the task is executed, it could not be invoked again (unless we reenable it).
But why should this to be a problem? We are running different tasks, aren't we? But actually we don't!
We run guard_dangerous_tasks
before all tasks in our tasks array! And it's being executed only once.
Solution #1 not the best one
As soon as we know where is our problem we can think about one (not the best solution).
Let's reenable guard_dangerous_tasks
after each iteration:
dangerous_task = Rake::Task['guard_dangerous_tasks']
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
dangerous_task.reenable
end
Solution #2 guard_dangerous_tasks
is not a prerequisite
We get better solution of our problem if we realize, that guard_dangerous_tasks
should not be a prerequisite! Prerequisite are supposed to "prepare" stage and be executed only once. But we should never blind our eyes to dangers!
This is why we should extend with guard_dangerous_tasks
as an action, which will be executed each time the parent task is run.
According to the source code of Rake::Task
(see above) we should pass our logic in a block if we want it to be added as an action.
%w[ db:setup db:reset ].each do |task|
Rake::Task[task].enhance do
Rake::Task['guard_dangerous_tasks'].execute
end
end
We can leave our test unchanged now and it passes:
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
But leaving invoke
is a ticket for new problems. It's better to be replaced with execute
:
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].execute }.to raise_error(InvalidTaskError)
end
Be careful with invoke
!
We said above, that using invoke
is a ticket for new problems. What kind of problems?
Let's try to test our code for both test
and production
environments. If we wrap our tests inside this loop:
['production','test'].each do |env_name|
env = ActiveSupport::StringInquirer.new(env_name)
allow(Rails).to receive(:env).and_return(env)
%w[ db:setup db:reset ].each do |task_name|
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
end
end
our test will fail with original reason. You can easily fix this by replacing the line
expect { Rake::Task[task_name].invoke }.to raise_error(InvalidTaskError)
with
expect { Rake::Task[task_name].execute }.to raise_error(InvalidTaskError)
So what was the reason? You probably already guess it.
In failing test we invoked the same two tasks twice. First time they were executed. The second time they should be reenabled before invokation to execute. When we use execute
, action is reenable automatically.
Note You can find working example of this project here: https://github.com/dimakura/stackoverflow-projects/tree/master/31821220-testing-rake
Related Topics
Did I Install Ruby 1.9.3 Correctly on Rhel
When Do You Need to Pass Arguments to 'Thread.New'
How to Implement Composite Primary Keys in Rails
Mocking Chain of Methods in Rspec
How to Refer a Local Gem in Ruby
How to Do Fuzzy Substring Matching in Ruby
Access Translation File (I18N) from Inside Rails Model
Rails 3. How to Explicitly Round a Number to Two Decimal Places in The Model
Can Multiple Sidekiq Instances Process The Same Queue
How to Read Post Data in Rack Request
Can You Create/Write/Append a String to a File in a Single Line in Ruby
Cannot Install Ruby Gems - Zlib Error
How to Combine a Series of PDFs into One Using Ruby
Ruby on Rails: Two References with Different Name to the Same Model
Disable Devise's :Confirmable On-The-Fly to Batch-Generate Users
How to Return Everything After Last Slash(/) in a Ruby String