简体   繁体   English

如何为包含包含“gets.chomp”的变量的 Ruby 方法编写 Rspec 测试?

[英]How do I write an Rspec test for a Ruby method that contains variable that contains 'gets.chomp'?

I've looked at other examples of tests, but most other examples don't necessarily have a variable that is equal to 'gets.chomp.downcase' and it's making it difficult for me to test.我查看了其他测试示例,但大多数其他示例不一定有一个等于“gets.chomp.downcase”的变量,这让我很难测试。

The rest is for a chess game but I'm trying to make it so if you type in "new" in the introduction, it'll call the method #instructions, which displays the instructions and asks if you're ready to play. rest 用于国际象棋游戏,但我正在努力做到这一点,因此如果您在介绍中输入“新”,它将调用方法#instructions,该方法会显示说明并询问您是否准备好下棋。

Here's the method #introduction这是方法#introduction

def introduction
        puts " \n"
        print "     Welcome to chess! "
        puts "What would you like to do?"
        puts "

      * Start a new Game  ->  Enter 'new'
      * Load a saved Game ->  Enter 'load'

      * Exit              ->  Enter 'exit'"
      input = gets.chomp.downcase
      if input == "new"
        instructions
      elsif input == "load"
        load_game
      elsif input == "exit"
        exit!
      else 
        introduction
      end
    end

Here's the test that I have for it that keeps displaying the error "Failure/Error: input = gets.chomp.downcase"这是我对其进行的测试,它不断显示错误“失败/错误:输入=gets.chomp.downcase”

"NoMethodError: undefined method `chomp' for nil:NilClass" “NoMethodError:nil:NilClass 的未定义方法‘chomp’”

describe Game do
    describe "#introduction" do
        it "starts a new game with input 'new'" do

            io = StringIO.new
            io.puts "new"

            $stdin = io

            game = Game.new
            game.introduction
            allow(game).to receive(:gets).and_return(io.gets)

            expect(game).to receive(:instructions)
        end
    end
end

Use Code Injection Instead of Mocks or Stubs使用代码注入代替模拟或存根

You have multiple problems with your approach.您的方法存在多个问题。 I won't enumerate them all, but instead focus on three key mistakes:我不会一一列举,而是关注三个关键错误:

  1. Unit tests should generally test method results , not replicate the internals.单元测试通常应该测试方法结果,而不是复制内部。
  2. You're trying to use #allow without defining a double first.您正在尝试使用 #allow 而不首先定义双精度。
  3. You seem to be trying to set a message expectation, instead of using a stub to return a value.您似乎正在尝试设置消息期望,而不是使用存根返回值。

There are certainly other problems with your code and your tests, but that's where I'd start once you remove your reliance on #gets from within your test cases.您的代码和测试肯定还有其他问题,但是一旦您从测试用例中消除对#gets 的依赖,我就会从这里开始。 For example, to test the various paths in your method, you should probably configure a series of tests for each expected value , where #and_return explicitly returns new , load , or whatever.例如,要测试方法中的各种路径,您可能应该为每个预期值配置一系列测试,其中 #and_return 显式返回newload或其他。

More pragmatically, you're most likely struggling because you wrote the code first, and now are trying to retrofit tests.更务实的是,您很可能会因为您先编写代码而苦苦挣扎,而现在正在尝试进行 retrofit 测试。 While you could probably monkey-patch things to make it testable post-facto, you're probably better off refactoring your code to allow direct injection within your tests.虽然您可能会修补一些东西以使其事后可测试,但您最好重构代码以允许在测试中直接注入。 For example:例如:

def show_prompt
  print prompt =<<~"EOF"

    Welcome to chess! What would you like to do?

      * Start a new Game  ->  Enter "new"
      * Load a saved Game ->  Enter "load"
      * Exit              ->  Enter "exit"

    Selection:\s
  EOF
end

def introduction input=nil
  show_prompt

  # Use an injected input value, if present.
  input ||= gets.chomp.downcase

  case input
  when "new"  then instructions
  when "load" then load_game
  when "exit" then exit!
  else introduction
  end
end

This avoids the need to stub or mock an object in the first place.这首先避免了存根或模拟 object 的需要。 Your tests can now simply call #introduction with or without an explicit value.您的测试现在可以简单地调用带有或不带有显式值的#introduction。 That allows you to spend your time testing your logic branches and method outputs, rather than writing a lot of scaffolding to support mocking of your IO#gets call or avoiding nil-related exceptions.这使您可以花时间测试逻辑分支和方法输出,而不是编写大量脚手架来支持 IO#gets 调用的 mocking 或避免与 nil 相关的异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM