3
votes

I try to feature test a Rake Task and capture its output:

This is a full integration test (feature test), which means I want to test it end-to-end. So I am not interested in stubbing out some ConsoleRenderer class, just testing that when I run the task, I see certain output in stdout.

The implementation, greatly simplified is

namespace :output do
  task :world do
    print 'hello world'
  end

  task :mars do
    print 'hello mars'
  end
end

And the feature test (rspec 3.3):

require 'feature_helper'

describe 'some output' do
  before do
    Rails.application.load_tasks
  end

  after do
    Rake::Task['output:world'].reenable
    Rake::Task['output:mars'].reenable
  end

  it 'outputs' do
    expect { Rake::Task['output:world'].invoke }.to output('hello world').to_stdout
  end

  it 'outputs again' do
    expect { Rake::Task['output:world'].invoke }.to output('hello world').to_stdout
  end

  it 'outputs mars' do
    expect { Rake::Task['output:mars'].invoke }.to output('hello mars').to_stdout
  end
end

When running this, somehow the output of the past runs is added up:

$ bundle exec rspec spec/features/output_spec.rb --order defined
.FF

Failures:

  1) some output outputs again
     Failure/Error: expect { Rake::Task['output:world'].invoke }.to output('hello world').to_stdout
       expected block to output "hello world" to stdout, but output "hello worldhello world"
     # ./spec/features/output_spec.rb:18:in `block (2 levels) in <top (required)>'

  2) some output outputs mars
     Failure/Error: expect { Rake::Task['output:mars'].invoke }.to output('hello mars').to_stdout
       expected block to output "hello mars" to stdout, but output "hello marshello marshello mars"
     # ./spec/features/output_spec.rb:22:in `block (2 levels) in <top (required)>'

To illustrate, I run the test in order of definition. The first test passes. The second fails, because somehow it includes the output of the $stdout in the previous test, or runs itself twice. Same for the third (which seems to run itself thrice).

I suspect this is something in the Rake task, because when I simplify the tests to merely print something, this adding does not occur:

it 'outputs' do
  expect { print 'hello world' }.to output('hello world').to_stdout
end

it 'outputs again' do
  expect { print 'hello world' }.to output('hello world').to_stdout
end

it 'outputs mars' do
  expect { print 'hello mars' }.to output('hello mars').to_stdout
end

This passes just fine.

Note that, on a console running

Rake::Task['output:mars'].invoke
Rake::Task['output:mars'].reenable
Rake::Task['output:mars'].invoke

Outputs "hello mars" once, then "hello mars" again. The issue does not reproduce itself in such an environment.

What do I need to reset, reenable, re-import, truncate or otherwise change, so that the output is not adding up?

2

2 Answers

2
votes

Turns out the reenable was not the right thing to call. Instead, I should have used clear. Which by its own documentation is "Normally used in the unit tests.".

after do
  Rake.application.clear
end

Note: This fixes it, but I still don't know why exactly nor how the setup as described in the question runs the tasks three times at some point. So an answer that explains why the behaviour is like it is and why running clear does not have that, will be preferred over my own answer.

0
votes

The reason it is executed twice or three times, is because you loaded the tasks before each block:

before do
  Rails.application.load_tasks
end

instead using before :all And optionally use Rails.application.clear before loading, because if you loaded the tasks in another spec, they will still be there