I am making a small Ruby program and cannot understand how to write RSpec specifications that mimic multiple user command line inputs (the functionality itself works). I think this StackOverflow answer probably covers the soil that is closest to where I am, but this is not exactly what I need. I use Thor for the command line interface, but I don't think this is a problem with anything in Thor.
The program can read commands either from a file or from the command line, and I was able to successfully write tests for reading in their execution. Here is the code:
cli.rb
class CLI < Thor # ... method_option :filename, aliases: ['-f'], desc: "name of the file containing instructions", banner: 'FILE' desc "execute commands", "takes actions as per commands" def execute thing = Thing.new instruction_set do |instructions| instructions.each do |instruction| command, args = parse_instruction(instruction) # private helper method if valid_command?(command, args) # private helper method response = thing.send(command, *args) puts format(response) if response end end end end # ... no_tasks do def instruction_set if options[:filename] yield File.readlines(options[:filename]).map { |a| a.chomp } else puts usage print "> " while line = gets break if line =~ /EXIT/i yield [line] print "> " end end end # .. end
I successfully tested the execution of the commands contained in the file with this code:
specifications / cli _spec.rb
describe CLI do let(:cli) { CLI.new } subject { cli } describe "executing instructions from a file" do let(:default_file) { "instructions.txt" } let(:output) { capture(:stdout) { cli.execute } } context "containing valid test data" do valid_test_data.each do |data| expected_output = data[:output] it "should parse the file contents and output a result" do cli.stub(:options) { { filename: default_file } }
and the above valid_test_data has the following form:
Support /utilities.rb
def valid_test_data [ { input: "C1 ARGS\r C2\r C3\r C4", output: "OUTPUT\n" }
What I want to do now is the same thing, but instead of reading each command from the "file" and executing it, I want to somehow imitate the user by entering text in stdin . The code below is completely wrong, but I hope it can convey the direction I want to go.
specifications / cli _spec.rb
# ... # !!This code is wrong and doesn't work and needs rewriting!! describe "executing instructions from the command line" do let(:output) { capture(:stdout) { cli.execute } } context "with valid commands" do valid_test_data.each do |data| let(:expected_output) { data[:output] } let(:commands) { StringIO.new(data[:input]).map { |a| a.strip } } it "should process the commands and output the results" do commands.each do |command| cli.stub!(:gets) { command } if command == :report STDOUT.should_receive(:puts).with(expected_output) else STDOUT.should_receive(:puts).with("> ") end end output.should include(expected_output) end end end end
I tried using cli.stub(:puts) in different places and, as a rule, changed this code many times, but it seems that I could not get any of my stubs to put the data in stdin. I do not know if I can analyze the set of inputs that I expect from the command line in the same way as with the command file, or what code structure I should use to solve this problem. If someone who developed command line applications could call back, that would be great. Thanks.