3

I'm testing a generator, which outputs a lot of stuff to STDOUT. I want to suppress this, and there are lots of answers for that.

But I want to still be able to use pry. Right now, I have to disable the suppression if I need to pry into the test state.

I was using this code. It bypassed pry entirely:

def suppress_output(&block)
  @original_stderr = $stderr
  @original_stdout = $stdout

  $stderr = $stdout = StringIO.new

  yield(block)

  $stderr = @original_stderr
  $stdout = @original_stdout
  @original_stderr = nil
  @original_stdout = nil
end

I replaced it with this. It stops at the pry, but continues to suppress output, so you can't do anything:

def suppress_output(&block)
  orig_stderr = $stderr.clone
  orig_stdout = $stdout.clone
  $stderr.reopen File.new("/dev/null", "w")
  $stdout.reopen File.new("/dev/null", "w")
  yield(block)
rescue Exception => e
  $stdout.reopen orig_stdout
  $stderr.reopen orig_stderr
  raise e
ensure
  $stdout.reopen orig_stdout
  $stderr.reopen orig_stderr
end

Is there any way to have my cake and eat it too?

Phillip Longman
  • 2,512
  • 2
  • 11
  • 15
  • What about another method `enable_output` that you can call from your pry session? – Stefan Apr 10 '18 at 07:30
  • That would still be kind of an annoying workaround. In this case, because of the library I'm using, I realized the best solution was to change the code, instead of the test setup. – Phillip Longman Apr 11 '18 at 03:44

2 Answers2

0

I'd still like an answer to this question if someone can think of a way. This isn't the only time I've had to suppress STDOUT in tests, and the scenarios haven't always been the same as this one.


However, it occurred to me today that in this case, the easier solution is to change the code, rather than the testing setup.

The generators are using Thor, which is very powerful, but has very opaque documentation past the basics and hasn't really been updated in years. When I dug around in the docs, I found there is some muting capability.

By calling add_runtime_options! in my main Cli < Thor class, I get a global --quiet option. This suppresses a lot of output, but not everything I need. #say still prints. #run itself is muted, but whatever shell commands I pass it to run are not.

Overwriting these methods takes care of the rest of my issues:

no_commands do
  def quiet?
    !!options[:quiet]
  end

  def run(command, config = {})
    config[:capture] ||= quiet?
    super(command, config)
  end

  def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
    super(message, color, force_new_line) unless quiet?
  end
end

I don't currently have a use-case where I would only want to suppress some things, so making it all-or-nothing works for now.

Now, I have to explicitly create the Cli instances in my tests with quiet: true, but I can run RSpec without unwanted output and still use pry.

Phillip Longman
  • 2,512
  • 2
  • 11
  • 15
0

I found this solution by Chris Hough to work in my case, adding the following configuration to spec/spec_helper:

RSpec.configure do |config|
  config.before(:each) do
    allow($stdout).to receive(:write)
  end
end

This replaced an :all before block, which was setting the following (and reversing the assignment in an after block):

$stderr = File.open(File::NULL, "w")
$stdout = File.open(File::NULL, "w")

The fix still suppresses output while allowing Pry to function as expected.

Dave Powers
  • 2,051
  • 2
  • 30
  • 34