Search code examples
ruby-on-railsrubyrspecrakehighline

Simplecov test coverage not reporting on Rake task spec


I have been battling why Simplecov is not reported this Rake task as covered when my specs work correctly via pry tests. Can anyone point me in the right direction?

The following rake task...

namespace :myapp do
  namespace :data do
    namespace :load do
      desc 'Enqueue a range of dates by provider'
      task range: :environment do
        cli = HighLine.new
        msg('Select the dates for your data pull. Format: yyyy-mm-dd')
        created_before = cli.ask('Data created_before? ', Date)
        created_after = cli.ask('Data created_after? ', Date)
        if created_after >= created_before
          error_msg('The created_after date must be less than created_before!')
          exit
        else
          Resque.enqueue(AmazonMws::ImportOrdersJob, created_before: created_before, created_after: created_after)
        end
      end

      desc 'Enqueue from a specified date by provider'
      task from: :environment do
        cli = HighLine.new
        msg('Select the date for your data pull. Format: yyyy-mm-dd')
        created_from = cli.ask('Data created_from? ', Date)
        Resque.enqueue(AmazonMws::ImportOrdersJob, created_from: created_from)
      end
    end
  end
end

The following spec...

describe 'rake task myapp:data:load:from' do
  include_context 'rake'
  let(:task_path) { 'tasks/data.rake' }

  before do
    ResqueSpec.reset!
  end

  describe ':range' do
    let(:created_before) { '2017-01-02' }
    let(:created_after) { '2017-01-01' }
    let(:task_name) { 'myapp:data:load:range' }
    before do
      invoke_task.reenable
      allow(highline).to receive(:ask).with('Data created_before? ', Date).and_return(created_before)
    end

    it 'exits if created_after is not >= created_before' do
      allow(highline).to receive(:ask).with('Data created_after? ', Date).and_return(created_before)
      expect { invoke_task.invoke }.to raise_error(SystemExit)
    end

    # rubocop:disable RSpec/MultipleExpectations
    it 'exits with the right message' do
      allow(highline).to receive(:ask).with('Data created_after? ', Date).and_return(created_before)

      expect do
        expect { invoke_task.invoke }.to output('The created_after date must be less than created_before!').to_stdout
      end.to raise_error(SystemExit)
    end
    # rubocop:enable all

    it 'processes from a specified starting point' do
      allow(highline).to receive(:ask).with('Data created_after? ', Date).and_return(created_after)
      invoke_task.invoke
      expect(AmazonMws::ImportOrdersJob).to have_queue_size_of(1)
    end
  end

  describe ':from' do
    let(:created_from) { '2017-01-01' }
    let(:task_name) { 'myapp:data:load:from' }
    before do
      invoke_task.reenable
      allow(highline).to receive(:ask).and_return(created_from)
    end

    it 'processes from a specified starting point' do
      # expect(Resque).to receive(:enqueue).with(AmazonMws::ImportOrdersJob, created_from: created_from)
      invoke_task.invoke
      expect(AmazonMws::ImportOrdersJob).to have_queue_size_of(1)
    end
  end
end

Shared context for rake tasks...

shared_context 'rake' do
  let(:invoke_task) { rake[task_name] }
  let(:highline) { instance_double(HighLine) }
  let(:rake) { Rake::Application.new }

  before do
    Rake.application = rake
    load File.expand_path("#{Rails.root}/lib/#{task_path}", __FILE__)
    Rake::Task.define_task(:environment)
    allow(HighLine).to receive(:new).and_return(highline)
    # rubocop:disable all
    allow_any_instance_of(Object).to receive(:msg).and_return(true)
    allow_any_instance_of(Object).to receive(:error_msg).and_return(true)
    # rubocop:enable all
  end
end

Updates

  • adjusted the spec in the question

enter image description here


Solution

  • SimpleCov uses Coverage#result module from Ruby standard library to check what was covered. Coverage#result gets reset for the file (this is my theory!) when you re-load the file using Kernel load.

    Since you are loading data.rake before each block gets executed, only the last spec in the rspec file gets reported in results.

    You can fix this by loading the tasks once before the all the examples are run using before(:all)

    A quick change in your shared context like below will give you the coverage report you want.

    RSpec.shared_context 'rake' do
      let(:invoke_task) { Rake.application[task_name] }
      let(:highline) { instance_double(HighLine) }
    
      before(:all) do
        Rake.application = Rake::Application.new
        Rails.application.load_tasks
      end    
    
      before do
        allow(HighLine).to receive(:new).and_return(highline)
        # rubocop:disable all
        allow_any_instance_of(Object).to receive(:msg).and_return(true)
        allow_any_instance_of(Object).to receive(:error_msg).and_return(true)
        # rubocop:enable all
      end
    end