为 rake 任务写 rspec
Write rspec for rake task
我有这样的任务/lib/crawler.rake
:
namespace :crawler do
area_names = Dir[Rails.root.join("lib", "crawler", "*.rb")].map do |file_name|
File.basename(file_name, ".rb")
end
area_names.each do |area_name|
task area_name.to_sym => :environment do
logger = Logger.new("log/crawl_#{area_name}.log")
# do something
parallel_results = crawler.crawl
mutex = Mutex.new
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
begin
# do something
rescue => e
# do something
raise e
end
end
Availability.update_by_grounds_and_time
end
end
end
逻辑在这里,如果并行后一切正常,我们将调用update_by_grounds_and_time
方法更新Availability
;如果出现错误,我们将停止操作并引发错误。
所以我想写 rspec 来测试这些情况,我想在这里 mock/stub 输出任务(通过或引发错误)并检查我们是否调用了 update_by_grounds_and_time
方法?
我们可以不需要调用真正的任务吗?我们可以使用 Rspec Mock
吗?
你能帮帮我吗!
谢谢
如果在Rakefile
中有定义,试试这个:
require 'rake'
RSpec.describe "Rake Tasks" do
before do
file, path = Rake.application.find_rakefile_location
Rake.load_rakefile("#{path}/#{file}")
end
it "should invoke some tasks" do
expect(Availability).to receive(:update_by_grounds_and_time)
Rake.application["crawler:#{area_name}"].invoke
end
end
如果在foo.rake
中有定义,那么试试这个:
require 'rake'
RSpec.describe "Rake Tasks" do
before do
Rake.application.rake_require('/path/to/lib/tasks/foo')
end
it "should invoke some tasks" do
expect(Availability).to receive(:update_by_grounds_and_time)
Rake.application["crawler:#{area_name}"].invoke
end
end
更新(错误案例)
例如
# foo.rake
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
begin
foo = Foo.new
foo.bar
# do something else
rescue => e
# do something
raise e
end
end
# foo_spec.rb
require 'rake'
RSpec.describe "Rake Tasks" do
before do
Rake.application.rake_require('/path/to/lib/tasks/foo')
end
it "should not call Availability#update_by_grounds_and_time if error raised" do
allow_any_instance_of(Foo).to receive(:bar).and_raise(StandardError)
expect(Availability).to_not receive(:update_by_grounds_and_time)
expect { Rake.application["crawler:#{area_name}"].invoke }.to raise_error(StandardError)
end
end
在这些情况下,我通常做的是将肉提取到一个单独的 class/service-object/whatever 中,这样更容易测试。然后 rake 任务就变成了该对象的调用者,因此不需要进行测试。
我有这样的任务/lib/crawler.rake
:
namespace :crawler do
area_names = Dir[Rails.root.join("lib", "crawler", "*.rb")].map do |file_name|
File.basename(file_name, ".rb")
end
area_names.each do |area_name|
task area_name.to_sym => :environment do
logger = Logger.new("log/crawl_#{area_name}.log")
# do something
parallel_results = crawler.crawl
mutex = Mutex.new
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
begin
# do something
rescue => e
# do something
raise e
end
end
Availability.update_by_grounds_and_time
end
end
end
逻辑在这里,如果并行后一切正常,我们将调用update_by_grounds_and_time
方法更新Availability
;如果出现错误,我们将停止操作并引发错误。
所以我想写 rspec 来测试这些情况,我想在这里 mock/stub 输出任务(通过或引发错误)并检查我们是否调用了 update_by_grounds_and_time
方法?
我们可以不需要调用真正的任务吗?我们可以使用 Rspec Mock
吗?
你能帮帮我吗! 谢谢
如果在Rakefile
中有定义,试试这个:
require 'rake'
RSpec.describe "Rake Tasks" do
before do
file, path = Rake.application.find_rakefile_location
Rake.load_rakefile("#{path}/#{file}")
end
it "should invoke some tasks" do
expect(Availability).to receive(:update_by_grounds_and_time)
Rake.application["crawler:#{area_name}"].invoke
end
end
如果在foo.rake
中有定义,那么试试这个:
require 'rake'
RSpec.describe "Rake Tasks" do
before do
Rake.application.rake_require('/path/to/lib/tasks/foo')
end
it "should invoke some tasks" do
expect(Availability).to receive(:update_by_grounds_and_time)
Rake.application["crawler:#{area_name}"].invoke
end
end
更新(错误案例)
例如
# foo.rake
Parallel.each(parallel_results, in_threads: [parallel_results.count, CRAWL_CONFIG["building_thread_max"]].min) do |pages|
begin
foo = Foo.new
foo.bar
# do something else
rescue => e
# do something
raise e
end
end
# foo_spec.rb
require 'rake'
RSpec.describe "Rake Tasks" do
before do
Rake.application.rake_require('/path/to/lib/tasks/foo')
end
it "should not call Availability#update_by_grounds_and_time if error raised" do
allow_any_instance_of(Foo).to receive(:bar).and_raise(StandardError)
expect(Availability).to_not receive(:update_by_grounds_and_time)
expect { Rake.application["crawler:#{area_name}"].invoke }.to raise_error(StandardError)
end
end
在这些情况下,我通常做的是将肉提取到一个单独的 class/service-object/whatever 中,这样更容易测试。然后 rake 任务就变成了该对象的调用者,因此不需要进行测试。