Ruby - Print with color output - ruby

I have a ruby script (Guardfile) that executes a rake command.
guard :shell do
watch(%r{^manifests\/.+\.pp$}) do |m|
spec = `rake spec`
retval = $?.to_i
case retval
when 0
if spec.length > 0 then
puts spec
n "#{m[0]} Tests Failed!", 'Rake Spec', :pending
else
puts spec
n "#{m[0]} Tests Passed!", 'Rake Spec', :pending
end
end
end
When I run a 'rake spec' from the command line, outputs are colorized.
How could I make it so the output of the ruby script is also colorized?
From command line:
From ruby script:
Update
I was able to sort-of work around the problem by using script
bash command preserve color when piping
spec = `script -q /dev/null rake spec`
This still has the downside of not scrolling the text in real time. While it does preserve the colors, it does not output anything until the very end.
Is there a more native way to do this that will allow for scrolling?

First, rake spec --color won't work, because you're passing --color to rake, and not rspec.
Jay Mitchell's suggestion for color should work - by putting this in your .rspec file:
--color
As for having "live" output, guard-shell has an eager command for this:
https://github.com/guard/guard-shell/blob/master/lib/guard/shell.rb#L37-L51
Unfortunately, guard-shell has 2 important shortcomings:
it doesn't give you access to the exit code
it doesn't properly report failures in Guard (which causes other tasks to run)
So the eager method of Guard::Shell is useless for our needs here.
Instead, the following should work:
# a version of Guard::Shell's 'eager()' which returns the result
class InPty
require 'pty'
def self.run(command)
PTY.spawn(command) do |r, w, pid|
begin
$stdout.puts
r.each {|line| $stdout.print line }
rescue Errno::EIO
end
Process.wait(pid)
end
$?.success?
rescue PTY::ChildExited
end
end
# A hack so that Guard::Shell properly throws :task_has_failed
class ProperGuardPluginFailure
def to_s
throw :task_has_failed
end
end
guard :shell, any_return: true do
watch(%r{^manifests\/.+\.pp$}) do |m|
ok = InPty.run('rake spec')
status, type = ok ? ['Passed', :success] : ['Failed', :failed]
n "#{m[0]} Tests #{status}!", 'Rake Spec', type
ok ? nil : ProperGuardPluginFailure.new
end
end
The above looks ideal for a new guard plugin - good idea?

I am unfamiliar with Guardfiles. Can you use gems? The colorize gem is great.
https://github.com/fazibear/colorize
Install it:
$ sudo gem install colorize
Use it:
require 'colorize'
puts "Tests failed!".red
puts "Tests passed!".green

Related

Suppress STDOUT during RSpec, but not Pry

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?
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.
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.

How can I appropriately mock out a method that returns yield?

It's fairly common in Ruby for methods that take blocks to look like this:
class File
def open(path, mode)
perform_some_setup
yield
ensure
do_some_teardown
end
end
It's also fairly idiomatic for a method to look like this:
def frobnicate
File.open('/path/to/something', 'r') do |f|
f.grep(/foo/).first
end
end
I want to write a spec for this that doesn't hit the filesystem, which ensures it pulls the right word out of the file, something like:
describe 'frobnicate' do
it 'returns the first line containing the substring foo' do
File.expects(:open).yields(StringIO.new(<<EOF))
not this line
foo bar baz
not this line either
EOF
expect(frobnicate).to match(/foo bar baz/)
end
end
The problem here is that, by mocking out the call to File.open, I've also removed its return value, which means that frobnicate will return nil. If I were to add something like File.returns('foo bar baz') to the chain, however, I'd end up with a test that doesn't actually hit any of the code I'm interested in; the contents of the block in frobnicate could do anything and the test would still pass.
How might I appropriately test my frobnicate method without hitting the filesystem? I'm not particularly attached to any particular testing framework, so if your answer is "use this awesome gem that'll do it for you" then I'm OK with that.
It seems like you just need to mock the call to File a little differently. I was getting syntax errors running your code as-is, so I'm not sure what version of RSpec you're on, but if you're on 3.x this will do the job:
frobnicate_spec.rb
gem 'rspec', '~> 3.4.0'
require 'rspec/autorun'
RSpec.configure do |config|
config.mock_with :rspec
end
def frobnicate
File.open('/path/to/something', 'r') do |f|
f.grep(/foo/).first
end
end
RSpec.describe 'frobnicate' do
it 'returns the first line containing the substring foo' do
allow(File).to receive(:open).and_call_original
allow(File).to receive(:open).and_yield StringIO.new <<-EOF
not this line
foo bar baz
not this line either
EOF
expect(frobnicate).to match(/foo bar baz/)
end
end
Invoke with ruby frobnicate_spec.rb so we can use a specified RSpec version.
Source: RSpec Mocks expecting messages and yielding responses
Using minitest it could be done like I post below. I have added the whole runnable file, so you can test it from the command line with ruby -Ilib:test test_file.rb:
def frobnicate
found_string = nil
File.open('/path/to/something', 'r') do |f|
found_string = f.grep(/foo/).first
end
found_string
end
class FrabnicateTest < Minitest::Test
def test_it_works
mock_file = StringIO.new(%(
not this line
foo bar baz
not hthis line either
))
search_result = nil
File.stub(:open, nil, mock_file) do
search_result = frobnicate
end
assert_match(/foo bar baz/, search_result)
end
end

Minitest - Tests Don't Run - No Rails

I'm just starting a small project to emulate a Carnival's ticket sales booth and one of the guidelines was to test that a user can enter the number of tickets. The program runs in the console and I eventually (hopefully) figured it out how to implement this test thanks to #Stefan's answer on this question.
The problem is that now, when I run the test file, minitest says:
0 runs, 0 assertions, 0 failures, 0 errors, 0 skips
I get the same result when I try to run the test by name using ruby path/to/test/file.rb --name method-name. I'm not sure if this is because my code is still faulty of if it's because I've set up minitest incorrectly. I've tried to look up similar problems on SO but most questions seem to involve using minitest with rails and I just have a plain ruby project.
Here's my test file:
gem 'minitest', '>= 5.0.0'
require 'minitest/spec'
require 'minitest/autorun'
require_relative 'carnival'
class CarnivalTest < MiniTest::Test
def sample
assert_equal(1, 1)
end
def user_can_enter_number_of_tickets
with_stdin do |user|
user.puts "2"
assert_equal(Carnival.new.get_value, "2")
end
end
def with_stdin
stdin = $stdin # global var to remember $stdin
$stdin, write = IO.pipe # assign 'read end' of pipe to $stdin
yield write # pass 'write end' to block
ensure
write.close # close pipe
$stdin = stdin # restore $stdin
end
end
In a file called carnival.rb in the same folder as my test file I have
Class Carnival
def get_value
gets.chomp
end
end
If anyone can help figure out why the test is not running I'd be grateful!
By convention, tests in Minitest are public instance methods that start with test_, so the original test has no actual test methods. You need to update your test class so that the methods with assertions follow the convention as:
class CarnivalTest < Minitest::Test
def test_sample
assert_equal(1, 1)
end
def test_user_can_enter_number_of_tickets
with_stdin do |user|
user.puts "2"
assert_equal(Carnival.new.get_value, "2")
end
end
# snip...
end
Yeah always start all your tests with test_ so it knows that you want to that function/method
class CarnivalTest < MiniTest::Test
def test_sample
assert_equal(1, 1)
end
def test_user_can_enter_number_of_tickets
with_stdin do |user|
user.puts "2"
assert_equal(Carnival.new.get_value, "2")
end
end
and that should work for you

Test output to command line with RSpec

I want to do is run ruby sayhello.rb on the command line, then receive Hello from Rspec.
I've got that with this:
class Hello
def speak
puts 'Hello from RSpec'
end
end
hi = Hello.new #brings my object into existence
hi.speak
Now I want to write a test in rspec to check that the command line output is in fact "Hello from RSpec"
and not "I like Unix"
NOT WORKING. I currently have this in my sayhello_spec.rb file
require_relative 'sayhello.rb' #points to file so I can 'see' it
describe "sayhello.rb" do
it "should say 'Hello from Rspec' when ran" do
STDOUT.should_receive(:puts).with('Hello from RSpec')
end
end
Can someone point me in the right direction please?
Here's a pretty good way to do this. Copied from the hirb test_helper source:
def capture_stdout(&block)
original_stdout = $stdout
$stdout = fake = StringIO.new
begin
yield
ensure
$stdout = original_stdout
end
fake.string
end
Use like this:
output = capture_stdout { Hello.new.speak }
output.should == "Hello from RSpec\n"
The quietly command is probably what you want (cooked into ActiveSupport, see docs at api.rubyonrails.org). This snippet of RSpec code below shows how to ensure there is no output on stderr while simultaneously silencing stdout.
quietly do # silence everything
commands.each do |c|
content = capture(:stderr) { # capture anything sent to :stderr
MyGem::Cli.start(c)
}
expect(content).to be_empty, "#{c.inspect} had output on stderr: #{content}"
end
end
So you don't have to change your main ruby code I just found out you can do something like this:
def my_meth
print 'Entering my method'
p 5 * 50
puts 'Second inside message'
end
describe '#my_meth' do
it 'puts a 2nd message to the console' do
expect{my_meth}.to output(/Second inside message/).to_stdout
end
end
When checking for a desired output text I used it inside / / like a Regexp because after many many maaany tests and looking around, the STDOUT is everything that is outputted so I found it to be better to use Regex so you could check the whole STDOUT for the exact text that you want.
Like I put it, it works in the terminal just perfect.
//Just had to share this, it took me days to figure it out.
it "should say 'Hello from Rspec' when run" do
output = `ruby sayhello.rb`
output.should == 'Hello from RSpec'
end

extending Rake's test tasks

I have a few pure-JavaScript, client-side tests using PhantomJS. These I'd like to integrate with rake test.
Currently I use this:
namespace :test do
task :client do
basedir = Rails.root.join("test", "client")
sh "cd #{basedir} && phantomjs lib/run-qunit.js index.html"
end
end
task :test => "test:client"
However, this integration is far from perfect; if one of these tests fails, rake aborts. Also, in contrast to :units, :functionals and :integration, there is no summary of the issues at the end (e.g. "6 tests, 21 assertions, 1 failures, 0 errors").
I could extract that data easily enough, but how do I tell Rake to add it to the total test tally?
You are calling via sh a shell command. Ruby does not know, that it is a test.
In addition sh seems to stop, if a failure occurs.
You have to do two things: Catch the error and check the result of your call.
An example:
require 'rake'
$summary = Hash.new(0)
def mytest(name, cmd)
$summary['test'] += 1
sh cmd do |ok, res|
if ok
$summary['ok'] += 1
else
$summary['failure'] += 1
puts "#{cmd } failed"
end
end
end
namespace :test do
task :one do |tsk|
mytest(tsk.name, "dir")
end
task :two do |tsk|
mytest(tsk.name, "undefined_cmd")
end
task :summary do
p $summary
end
end
task :test => "test:one"
task :test => "test:two"
task :test => "test:summary"
shis called with a block to catch failures. Inside the block, I analyse the result (true for ok, false if the script stops with an error. The result is added to a summary hash.
For your use, you may adapt the code and split the code into two files: All test in one file. And the rake file get a Rake::TestTast.
Your test file may look like this:
gem 'test-unit'
require 'test/unit'
class MyTest < Test::Unit::TestCase
def test_one
assert_nothing_raised{
basedir = Rails.root.join("test", "client")
res = system("cd #{basedir} && phantomjs lib/run-qunit.js index.html")
assert_true(res)
}
end
def test_two
assert_nothing_raised{
res = `dir` #Test with windows
assert_match(/C:/, res) #We are in c:
}
end
end
This works only, if your test finish with a exit code. Perhaps you can use `` instead and get the output of your test for a detailed analyze.

Resources