Hash RSpec can';尝试模拟哈希元素的get和put时,无法定义单例错误

Hash RSpec can';尝试模拟哈希元素的get和put时,无法定义单例错误,hash,io,mocking,rspec2,ruby-1.9.3,Hash,Io,Mocking,Rspec2,Ruby 1.9.3,我有一个图书模型,它是一个ruby脚本,为程序中提到的某些预定义图书标题指定价格。以下是书本模型的外观:- class Book attr_accessor :books def initialize books puts "Welcome to setting book price program" @books = books end def get_prices puts "Please enter appropriate price for

我有一个图书模型,它是一个ruby脚本,为程序中提到的某些预定义图书标题指定价格。以下是书本模型的外观:-

class Book

  attr_accessor :books  
  def initialize books
    puts "Welcome to setting book price program"
    @books = books
  end

  def get_prices
    puts "Please enter appropriate price for each book item:-"
    count = 0
    @books = @books.inject({}) { |hash, book|
      print "#{book.first}: "
      price = STDIN.gets.chomp
      while (price !~ /^[1-9]\d*$/ && price != "second hand")
        puts "Price can't be 0 or a negative integer or in decimal format or alphanumeric. \nPlease input appropriate price in integer"
        price = STDIN.gets.chomp #gets.chomp - throws error
      end
      price == "second hand" ? price = "100" : price #takes a default price
      hash[book.first] = price.to_i
      hash
    }
  end

end

books = {"The Last Samurai" => nil,
         "Ruby Cookbook" =>  nil,
         "Rails Recipes" =>  nil,
         "Agile Development with Rails" =>  nil,
         "Harry Potter and the Deathly Hallows" =>  nil}


book_details = Book.new(books)
book_details.get_prices
puts "\n*******Books Details:#{book_details.books}******\n"
我正试图编写一个测试用例,检查每本书的价格输入是否正确。如果价格输入不正确,应要求用户重新正确输入价格。这个程序做得很好。但是当我试图用RSpec来模拟这种行为时,我遇到了困难

require 'spec_helper'

describe Book do

  before :each do
    books = {"The Last Samurai" => nil,
         "Ruby Cookbook" =>  nil,
         "Rails Recipes" =>  nil,
         "Agile Development with Rails" =>  nil,
         "Harry Potter and the Deathly Hallows" =>  nil}
    @book = Book.new(books)
  end

  describe "#new" do
    it "Should be an instance of the Book" do
      @book.should be_an_instance_of Book
    end
  end

  describe "#getprice" do
    it "Should get the price in the correct format or else return appropriate error" do
      puts "\n************************************************************************\n"
      book_obj = @book
      STDOUT.should_receive(:puts).and_return("Welcome to setting book price program")
      book_obj.get_prices.should_not be_nil
      book_obj.books["The Last Samurai"].stub!(:gets) {"40"} #trying to set the value for one book using Hash
      book_obj.books["The Last Samurai"].should == 40 #verifying the value set for a particular key is accurate
    end
  end

end
您甚至可以从Github尝试从您的终端执行此操作。我正在使用Ruby1.9.3和RSpec2.11.0

The error that I'm getting currently is:-

Failures:

  1) Book#getprice Should get the price in the correct format or else return appropriate error
     Failure/Error: book_obj.books["The Last Samurai"].stub!(:gets) {"40"} #trying to set the value for one book using Hash
     TypeError:
       can't define singleton
     # ./spec/book_spec.rb:31:in `block (3 levels) in <top (required)>'

Finished in 7.61 seconds
2 examples, 1 failure

Failed examples:

rspec ./spec/book_spec.rb:21 # Book#getprice Should get the price in the correct format or else return appropriate error
我当前遇到的错误是:-
失败:
1) Book#getprice应以正确的格式获取价格,否则返回相应的错误
失败/错误:book_obj.books[“最后的武士”]存根!(:gets){“40”}#尝试使用哈希设置一本书的值
类型错误:
无法定义单例
#./spec/book_spec.rb:31:in'block(3层)in'
以7.61秒完成
2例,1例失败
失败的示例:
rspec./spec/book_spec.rb:21#book#getprice应以正确的格式获取价格,否则返回相应的错误
更新的问题

对于错误的用户输入,使用下面的测试用例,我得到以下错误。我怎样才能正确处理这个问题?我试过几种选择,但似乎都失败了。请将每个选项的注释作为规范代码段的一部分查看

 it "Incorrect input format should return error message asking user to re input" do
      puts "\n************************************************************************\n"
      book_obj = @book
      STDIN.stub(:gets) { "40abc" }

      #book_obj.get_prices.should be_nil --> adding this line of code goes into an infinite loop with the error message below
      #Price cannot be 0 or a negative integer or in decimal format or alphanumeric. \nPlease input appropriate duration in integer\n

      STDOUT.should_receive(:puts).and_return("Price cannot be 0 or a negative integer or in decimal format or alphanumeric. \nPlease input appropriate duration in integer\n")

      #the below two tests fails with syntax error - don't seem that easy to figure out what's going wrong

      #STDOUT.should_receive("Price cannot be 0 or a negative integer or in decimal format or alphanumeric. \nPlease input appropriate duration in integer\n")
      #STDOUT.should == "Price cannot be 0 or a negative integer or in decimal format or alphanumeric. \nPlease input appropriate duration in integer\n"
    end


Failures:

  1) Book#getprice Incorrect input format should return error message asking user to re input
     Failure/Error: STDOUT.should_receive(:puts).and_return("Price cannot be 0 or a negative integer or in decimal format or alphanumeric. \nPlease input appropriate duration in integer\n")
       (#<IO:0x00000001c7b298>).puts(any args)
           expected: 1 time
           received: 0 times
     # ./spec/book_spec.rb:40:in `block (3 levels) in <top (required)>'
it“不正确的输入格式应返回错误消息,要求用户重新输入”do
放入“\n*********************************************************************************************************************************************\n”
book_obj=@book
STDIN.stub(:get){“40abc”}
#book_obj.get_prices.should_nil-->添加这行代码将进入无限循环,并显示以下错误消息
#价格不能为0或负整数,也不能为十进制或字母数字格式\n请以整数形式输入适当的持续时间\n
STDOUT.should\u receive(:put.)and\u return(“价格不能是0或负整数,也不能是十进制格式或字母数字格式。\n请在整数中输入适当的持续时间\n”)
#下面的两个测试因语法错误而失败-要找出错误似乎并不那么容易
#STDOUT.should\u receive(“价格不能是0或负整数,也不能是十进制格式或字母数字格式。\n请在整数中输入适当的持续时间\n”)
#STDOUT.should==“价格不能是0或负整数,也不能是十进制格式或字母数字格式。\n请在整数中输入适当的持续时间\n”
结束
失败:
1) Book#getprice不正确的输入格式应返回错误消息,要求用户重新输入
失败/错误:STDOUT.should\u receive(:put.)和\u return(“价格不能是0或负整数,也不能是十进制格式或字母数字格式。\n请以整数输入适当的持续时间\n”)
(#)放置(任何参数)
预计:1次
收到:0次
#./spec/book_spec.rb:40:in'block(3层)in'

我真的很感激任何关于如何做到这一点的指导。谢谢。

如果使用
--backtrace
标志运行规范,您将看到在上引发了错误,其中rspec mocks正在尝试获取被存根或模拟的对象的单例类。这对于大多数类的实例来说都很好,但是规范中有一个错误

代码将
gets
发送到
STDIN
,但是规范试图在
book\u obj.books[“最后的武士”]
上存根
gets
,这在当时是一个
int
,并且您无法从
int
中获得一个单件:

$ irb
1.9.3-p392 :001 > class << 1; self; end
TypeError: can't define singleton
        from (irb):1
    from /Users/david/.rvm/rubies/ruby-1.9.3-p392/bin/irb:16:in `<main>'
这至少会让你的规格通过


一般来说,直接处理w/
STDIN
STDOUT
的代码很难在对象级别进行规范,因为诸如rspec、minitest等测试工具都使用
STDOUT
来表示它们的信息,因此您最终会在shell中遇到许多令人困惑的噪音。我建议更改设计,将输入/输出流注入book类(运行脚本时可以是
STDIN
STDOUT
,但运行rspec时测试加倍),或者使用类似的工具,设计用于规范交互式shell脚本。

首先,非常感谢你帮我解决这个问题。您的建议确实有助于处理以正确格式提供的用户输入。尽管我尝试了几个组合,但我仍然无法解决糟糕的用户输入问题(请参阅更新的问题,其中包含代码和错误的详细信息)。我还有一件事要问,如果我遗漏了什么,请原谅我的无知。。当我试图在book_obj.books[“Last Samurai”]上存根时,我只是想给key赋值,所以我真的不理解int是什么。。请解释..谢谢无限循环是因为
中的
循环,而
获取价格
中的
循环。您需要同时对错误输入和正确输入进行存根处理,以通过它,例如
STDIN.stub(:gets){“40abc”};STDIN.stub(:get){“40”}
。语法错误是因为您说
STDOUT
应该接收字符串,但它应该接收带有字符串的
put
,例如
STDOUT.should\u receive(:put).with(“this String”)
。您正在尝试查找一个名为“Price不能为0或负整数,也不能为十进制格式或字母数字格式。\n请用整数\n输入适当的持续时间”的方法,因此出现语法错误。David,根据您对错误输入规范的建议,我已更新了该文件。请看一下变化。我仍然得到无限循环,我猜规范没有到达我可以检查以下条件的那一行(即通过链接共享的实际代码中的第33行)——STDOUT.should_receive(:puts)。使用(“此字符串”)可能我需要首先正确处理无限循环,但不知何故,我还无法到达那里。Th
STDIN.stub(:gets) { "40" }
book_obj.get_prices.should_not be_nil