【问题标题】:Using RSpec to Test Console Application Interaction使用 RSpec 测试控制台应用程序交互
【发布时间】:2016-06-23 06:06:15
【问题描述】:

我有几个控制台应用程序,它们使用 readline 进行读取、评估、打印、循环 (REPL),如果可能的话,我想为使用 RSpec 创建测试。我读过关于模拟课程的文章,但我不知道它们是如何工作的。是否只能替换一个真正的输入类来测试其余的类?我想对整个应用程序进行集成测试。对于我的一个 Web 服务,我有一个 rspec 脚本,它使用 curl 发送请求以测试套接字接口。我想为控制台界面做类似的事情。我只是不了解模拟类的概念吗?

回答

根据@Dave Schweisguth 给出的答案,我尝试了这个:

require 'rspec'
require 'pty'

describe "anthematic" do
  before(:all) do
    @output, @input = PTY.spawn('bin/anthematic')
  end

  it "turns on a zone" do
    @output.readpartial 1024 # read past the prompt
    @input.puts "on 1"
    expect(@output.readline.chomp).to eq("on 1")
    expect(@output.readline.chomp).to eq("turn on zone 1")
  end
end

我将 spawn 移出,以便添加其他测试。我是 rspec 新手,所以这可能不是最干净的解决方案。

在研究 PTY 时,我发现 this article 涉及 popen、pty 等子进程。

【问题讨论】:

    标签: ruby unit-testing rspec console-application read-eval-print-loop


    【解决方案1】:

    我同意您的观点,最好对整个应用程序进行至少一项或多项集成测试。这些测试不应该有模拟;模拟替换要单独测试的类的依赖项。下面是一个集成测试命令行应用程序的简单方法,以 irb 为例:

    require 'pty'
    
    describe "irb" do
      it "evaluates an expression" do
        PTY.spawn('irb') do |output, input|
          output.readpartial 1024 # read past the prompt
          input.puts "1 + 1"
          expect(output.readline.chomp).to eq("1 + 1")
          expect(output.readline.chomp).to eq("=> 2")
        end
      end
    end
    

    如果我有不止一个这样的测试,我会提取一个 RSpec 匹配器,但为了简单起见,我在这里留下了细节内联。

    这种测试比较慢,因为它使用新的 Ruby 实例在单独的进程中运行应用程序,因此您只想编写其中的一个或几个来测试应用程序是否可以作为一个整体运行,然后进行测试各个类的单元测试的详细信息。这些看起来像什么,以及您是否需要模拟,完全取决于您应用的内部结构。

    【讨论】:

    • Dave Schweisguth,你的例子对我来说很重要。我把细节放在我的问题中。
    • 尝试删除第一个stdout.readline。这会丢弃你的程序没有的一行 irb 输出。
    • 该更改无效。我按原样尝试了您的代码,并且可以正常工作。我将在今天晚些时候阅读 popen3。
    • 我将答案更改为使用 pty,这对 irb 更自然。可能它也会对你更好。
    【解决方案2】:

    所以模拟在 rspec 测试中的作用是代替方法调用并给出预期的返回值。所以说在这种情况下你有一个调用来获取哈希的类,但你不想在你的规范中实际调用。

    MyClass.get_info
    

    或类似的东西。

    在 rpsec 中,您可以指定调用该方法时的返回值/对象/任何内容。为此,请在规范的开头或之前的块中放置

    allow(MyClass).to receive(:get_info).and_return({ key: 'value' })
    

    现在在规范中,对 MyClass.get_info 的任何调用都会返回您指定的哈希值。

    my_info = MyClass.get_info
    # my_info = { key: 'value' }
    

    【讨论】:

      【解决方案3】:

      当某些东西太难实际测试或太耗费资源而无法测试时,你基本上会编写一个模拟。

      在这种情况下,您正在尝试做的事情(REPL 测试)是困难的,但并非不可能。

      我确信还有其他方法可以做到这一点,但这是我使用过的方法:

      第 1 步编写一个 shell 命令,将命令通过管道传送到您的 REPL 程序。

      shell_cmd = %{(echo "MyClass.new.call_my_command"; echo "exit") | ruby my_repl_program.rb"}
      

      这使用括号组合多个“回声”输出,然后发送到您的程序。请注意,您需要发送退出命令,否则进程将挂起。

      第 2 步调用 shell 命令并为输出设置一个变量:

      cmd_output = `#{shell_cmd}`
      

      第 3 步验证输出

      现在您可以根据需要测试cmd_output 的值。

      【讨论】:

        猜你喜欢
        • 2013-07-28
        • 2012-02-27
        • 1970-01-01
        • 2013-04-09
        • 2012-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多