Test CLI with parameters - ruby

I assume this is very newbie stuff but I'm learning Ruby by doing, and I'm developing a small CLI tool that receives a couple of parameters in order to do its stuff properly. This is my current workflow:
I want to test (using Minitest) all the possible flows:
Exits with 0 and help message is shown if ARGV.count != 2
Exits with 1 if first param is not correct
Exits with 1 if second param is not correct
Exits with 1 if both params are not correct
Exits with 0 and does stuff if all params are correct
Now, if I run tests the only thing I see is the help output as there is no parameter being passed.
So, a couple of questions:
How can I pass arguments to the main program in tests?
How can I test the output? (I'm using puts)
Thanks!

nice diagram!
you can either use helpers like aruba https://github.com/cucumber/aruba
or dig into ruby internals in order to bend it to your will!
# test.rb
pseudoIO = StringIO.new
$stdout = pseudoIO
puts "hi #{ARGV.join(', ')}"
ARGV.replace ["file1"]
puts "now its #{ARGV.join(', ')}"
abort "captured: #{pseudoIO.string}"
output should be
ruby text.rb "whutup"
# => captured: hi whutup
# => now its file1

Related

How to use a conditional within a Ruby expect script

I'm playing with expect in Ruby but I'm a little lost as to how I can branch my code based on the behavior of a device I am logging into. How could I do say foo.run if I get the correct prompt below > but run foo.fail if I do not? Even further, how can I evaluate all of the text that comes back between entering the password and receiving the > prompt? Is there a way to look at all text that the device prints somehow?
def device_test(password)
$expect_verbose = true
PTY.spawn("ssh my-router") do |reader, writer, pid|
reader.expect("password:")
writer.puts(password)
reader.expect(">")
puts "logged in"
sleep(15)
end
end
It appears that the expect method can only look for a single pattern (unlike the Tcl expect library where you can look for multiple patterns simultaneously).
It looks like you'll have to pass a "timeout" parameter and check the return value:
if reader.expect(">", 2)
puts "foo.run"
else
# did not see ">" within 2 seconds
puts "foo.fail
end

RSpec and Testing Command Line Args Passed to Script

I'm some rspec tests for a command line ruby based application. I'm trying to build my test suite allow for testing of missing command line parameters. Specifically, I'd like to stub out what ARGV[0]..ARGV[N] would appear to the application. I've seen similar posts mention ENV.stub; however, I don't see how I can simulate "nameless" args and a given order.
Any help would be appreciated. Thanks.
I simply did this in my test case located in spec:
it "does something" do
arg = "/tmp"
exe = File.expand_path('../bin/site_checker', File.dirname(__FILE__))
stdin, stdout, stderr = Open3.popen3("#{exe} -Ilib #{arg}")
stdout.readlines.should be_empty
end

Why does cucumber with aruba not see the output of my program?

Both examples are going to STDOUT, but cucumber only sees the first one. The second scenario fails with:
Then the stdout should contain "test" # aruba-0.4.11/lib/aruba/cucumber.rb:82
expected "" to include "test" (RSpec::Expectations::ExpectationNotMetError)
features/test.feature:13:in `Then the output should contain "test"'
The features:
Scenario: echo test
Given a blank slate
When I run `echo "test"`
The stdout should contain "test"
Scenario: puts test
Given a blank slate
When I start the program
The stdout should contain "test"
The step definitions:
When /^I start the program$/ do
TestModule::Main.new.start
end
The code:
module TestModule
class Main
def initialize
end
def start
$stdout.puts "test"
end
end
end
I'm not that familiar with Aruba, but a quick peek into it's source code suggests that the assertions it makes against STDOUT (or any output) only apply to processes that it started itself, and not all content that's been written to STDOUT. The code that you invoke yourself, in the second scenario, is outside of the control of Aruba, so it's output won't be tracked.
If you think about it, it couldn't really work any other way - if Aruba captured all STDOUT for assertions, then it would contain Cucumber's own test output as well...
It looks like you're trying to test your program in-process without using Aruba to invoke a separate Ruby process. If that's the case I'd suggest modifying the program to make it possible to pass in a STDOUT replacement e.g.
def initialize(output=$stdout)
Then when you start the code:
When /^I start the program$/ do
TestModule::Main.new(#output).start
end
And you can change your assertion:
Then the stdout should contain "(.+)" do |string|
#output.should include string
end

How do you write and execute Ruby tests in a Ruby interpreter?

I was working with the lotrepls Ruby interpreter, and I want like to write tests in the interpreter that I can then write Ruby code to pass. In Python, I can write doctests and then write code to pass the doctests. For example:
>>> b
1
This tests that b=1, and entering b=1 will get this doctest to pass.
Is there a similar way to write tests in a Ruby interpreter, execute them, write code to pass the tests, and then execute the test again? Is there a Ruby doctest equivalent? For my application, I will execute tests and code in a hosted interpreter like lotrepls rather than install something on my local machine.
There's RubyDocTest, but I'd encourage you to look at something like RSpec or another modern BDD/TDD framework.
It's pretty easy to write tests there too, and you get access to complex and/or custom assertions that you can't really get in a doctest. For instance, here's a simple set of tests for a baseball scoring app:
describe BaseballScorer do
before :each do
#s = Scorer.new(Game.new)
end
it "should score a 0-0 game when no runs are hit" do
#s.home.score.should == #s.away.score.should == #s.total_runs
end
it "should record runs that are hit" do
#s.game.run_hit(:away)
#s.away.runs.should == #s.away.score.should == 1
end
# ...
This is a little old post but I faced the same problem some months ago.
John's answer is correct but if you want to use something similar to a irb session you could try byexample, in particular it supports Ruby
For example you can write a Markdown doc like this:
This is an awesome expression:
```ruby
>> 1 + 2
=> 3
```
Then you just run from the shell
$ byexample -l ruby your-markdown-doc.md
[PASS] Pass: 1 Fail: 0 Skip: 0
You could also embed the test inside a Ruby comment like
# square 2
# => 4
def square x
x * x
end
And that's it. The example is executed and checked so your doc works as regression test as well.
Disclaimer: Like I said, I had the same desire to do TDD in Ruby so I wrote byexample. I really hope than others find it as much as useful and I do.

check for (the absence of) `puts` in RSpec

I am using rspec for my test in a ruby project, and I want to spec that my program should not output anything when the -q option is used. I tried:
Kernel.should_not_receive :puts
That did not result in a failed test when there was output to the console.
How do I verify the absents of text output?
puts uses $stdout internally. Due to the way it works, the easiest way to check is to simply use: $stdout.should_not_receive(:write)
Which checks nothing is written to stdout as expected.
Kernel.puts (as above) would only result in a failed test when it
is explictely called as such (e.g. Kernel.puts "Some text"), where
as most cases it's call in the scope of the current object.
The accepted answer above is incorrect. It "works" because it doesn't receive a :write message but it might have received a :puts message.
The correct line should read:
$stdout.should_not_receive(:puts)
Also you need to make sure you put the line before the code that will write to STDIO. For instance:
it "should print a copyright message" do
$stdout.should_receive(:puts).with(/copyright/i)
app = ApplicationController.new(%w[project_name])
end
it "should not print an error message" do
$stdout.should_not_receive(:puts).with(/error/i)
app = ApplicationController.new(%w[project_name])
end
That's an actual working RSpec from a project

Resources