4

プログラムで言及されている特定の事前定義された本のタイトルに価格を割り当てるルビ スクリプトである本モデルがあります。本のモデルは次のようになります。

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 からこのコードを複製して、自分の側からこれを試すこともできます。Ruby 1.9.3 と rspec 2.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

更新された質問

悪いユーザー入力の場合、以下のテストケースで次のエラーが発生します。どうすればこれを正しく処理できますか? いくつかのオプションを試しましたが、すべて失敗しているようです。仕様スニペットの一部として、各オプションのコメントを参照してください。

 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)>'

これを正しく行う方法についてのガイダンスをいただければ幸いです。ありがとうございました。

4

1 に答える 1

5

フラグを指定して仕様を実行すると、https://github.com/rspec/rspec-mocks/blob/v2.13.0/lib/rspec/mocks/method_double.rb#L140--backtraceでエラーが発生することがわかります。 、ここで rspec-mocks は、スタブ化またはモック化されているオブジェクトのシングルトン クラスを取得しようとしています。これはほとんどのクラスのインスタンスで問題なく機能しますが、仕様に誤りがあります。

コードは に送信getsしますが、スペックはその時点でであるSTDINにスタブしようとしています。 からシングルトンを取得することはできません:getsbook_obj.books["The Last Samurai"]intint

$ 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>'

私の理解が正しければ、あなたは 2 つの変更を加えたいと考えています。まず、スクリプトの最後の行を、spec を実行したときにロードされない別のファイル (bin/books など) に移動します (book.rb が必要で、それらの行を追加できます)。

次に、スタブになっている行を削除し、gets代わりにスタブになっている行を、呼び出す行のbook_obj.books["The Last Samurai"]に追加します(これは、対話が発生するときです)。getsSTDINget_pricesSTDIN

STDIN.stub(:gets) { "40" }
book_obj.get_prices.should_not be_nil

それは少なくともあなたの仕様に合格するでしょう。

一般に、 rspec、minitest などのテスト ツールはすべてそれらの情報を表示するために使用されるため、オブジェクト レベルでw/STDINおよび直接処理するコードを仕様化するのは困難です。book クラスに入力/出力ストリームを注入するように設計を変更することをお勧めします (これは、スクリプトを実行するときに実行できますが、rspec を実行するとダブルスをテストします) 対話型シェル スクリプト。STDOUTSTDOUTSTDINSTDOUT

于 2013-07-20T11:59:50.537 に答える