如何使用rspec测试CLI的stdin [英] How to test stdin for a CLI using rspec

查看:117
本文介绍了如何使用rspec测试CLI的stdin的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个小的Ruby程序,无法弄清楚如何编写RSpec规范来模拟多个用户命令行输入(该功能本身有效).我认为此StackOverflow答案可能涵盖了最接近我所在的地面,但并不是我所需要的.我在命令行界面上使用了 Thor ,但是我不认为这是一个问题雷神中的一切.

I'm making a small Ruby program and can't figure out how to write RSpec specs that simulate multiple user command line inputs (the functionality itself works). I think this StackOverflow answer probably covers ground that is closest to where I am, but it's not quite what I need. I am using Thor for the command line interface, but I don't think this is an issue with anything in Thor.

该程序可以从文件或命令行中读取命令,并且我已经能够成功编写测试以读取并执行它们.这是一些代码:

The program can read in commands either from a file or the command line, and I've been able to successfully write tests to read in an execute them. Here's some 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've tested successfully for executing commands contained in a file with this code:

spec/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 } } # Thor options hash
          File.stub(:readlines).with(default_file) do
            StringIO.new(data[:input]).map { |a| a.strip.chomp }
          end
          output.should == expected_output
        end
      end
    end
  end
  # ...
end

和上面提到的valid_test_data的格式如下:

and the valid_test_data referred to above is in the following form:

support/utilities.rb

def valid_test_data
  [
    {
      input: "C1 ARGS\r
              C2\r
              C3\r
              C4",
      output: "OUTPUT\n"
    }
    # ...
  ]
end

我现在想做的是完全相同的事情,但是我不想以某种方式模拟用户键入stdin的情况,而不是从文件"中读取每个命令并执行它.下面的代码完全错误,但是我希望它可以传达我想去的方向.

What I want to do now is exactly the same thing but instead of reading each command from the 'file' and executing it, I want to somehow simulate a user typing in to stdin. The code below is utterly wrong, but I hope it can convey the direction I want to go.

spec/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

我尝试在不同的地方使用cli.stub(:puts),并且通常在很多地方重新排列此代码,但似乎无法让我的任何存根都将数据放入stdin中.我不知道是否可以像处理命令文件一样解析命令行中期望的输入集,或者我应该使用哪种代码结构来解决此问题.如果已经指定命令行应用程序的人可以加入,那就太好了.谢谢.

I've tried using cli.stub(:puts) in various places, and generally rearranging this code around a lot, but can't seem to get any of my stubs to put data in stdin. I don't know if I can parse the set of inputs I expect from the command line in the same way as I do with a file of commands, or what code structure I should be using to solve this issue. If someone who has spec-ed up command-line apps could chime in, that would be great. Thanks.

推荐答案

我最终找到了一个解决方案,我认为该解决方案与用于执行文件中指令的代码非常相似.我终于意识到我可以编写cli.stub(:gets).and_return并将其传递到要执行的命令数组中(作为参数,感谢splat *运算符),然后将"EXIT"命令传递给结束.如此简单,却难以捉摸.非常感谢此StackOverflow问题和答案,因为我把我逼到了门外.

I ended up finding a solution that I think fairly closely mirrors the code for executing instructions from a file. I overcame the main hurdle by finally realizing that I could write cli.stub(:gets).and_return and pass it in the array of commands I wanted to execute (as parameters thanks to the splat * operator), and then pass it the "EXIT" command to finish. So simple, yet so elusive. Many thanks go to this StackOverflow question and answer for pushing me over the line.

这是代码:

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
        cli.stub(:gets).and_return(*commands, "EXIT")
        output.should include(expected_output)
      end
    end
  end
  # ...
end

这篇关于如何使用rspec测试CLI的stdin的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆