2013-07-20 1 views
4

У меня есть модель книги, которая представляет собой рубиновый скрипт, который присваивает цены определенным предопределенным названиям книг, упомянутым в программе. Вот как книга выглядит модель: -RSpec не может определить одноэлементную ошибку при попытке подделать get и put для хэш-элементов

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 

Вы можете даже clone this code от 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)>' 

Я бы очень признателен за любые рекомендации, как правильно это сделать. Спасибо.

ответ

5

Если вы запустите спецификацию с флагом --backtrace, вы увидите, что ошибка поднята на https://github.com/rspec/rspec-mocks/blob/v2.13.0/lib/rspec/mocks/method_double.rb#L140, где rspec-mocks пытается получить одноэлементный класс обрезанного или издеваемого объекта. Это будет нормально работать с экземплярами большинства классов, но в спецификации есть ошибка.

код посылает gets в STDIN, но спецификация пытается окурок gets на book_obj.books["The Last Samurai"], который является 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>' 

Если Я правильно понимаю, вы хотите сделать два изменения. Сначала переместите последние строки сценария в отдельный файл, который не загружается при запуске спецификации, например. bin/books (для этого может потребоваться book.rb, а затем добавить эти строки).

Затем удалите строку, окурки gets на book_obj.books["The Last Samurai"] и добавить строку, которая окурки gets на STDIN вместо того, чтобы, перед тем линию, которая вызывает get_prices (что, когда взаимодействие ж/STDIN бывает):

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

Это, по крайней мере, даст вам пропуск.

В общем, код, который имеет дело ж/STDIN и STDOUT непосредственно трудно спецификации на уровне объекта, так как инструменты тестирования, как RSpec, MiniTest и т.д., все используют STDOUT представить свою информацию, так что вы в конечном итоге с большим количеством путающий шум в оболочке.Я бы рекомендовал либо сменить дизайн, чтобы вводить потоки ввода/вывода в класс книги (которые могут быть STDIN и STDOUT при запуске скрипта, но при удвоении теста при запуске rspec) или использовать такой инструмент, как aruba, который разработан для спецификации интерактивных сценариев оболочки.

+0

Прежде всего, очень признателен, что вы пытаетесь помочь мне обойти это. Ваше предложение действительно помогло обработать ввод данных пользователя в правильном формате. Я по-прежнему не в состоянии понять, что происходит с плохим пользовательским вводом, пытаясь попробовать пару комбинаций (см. ** обновленный вопрос ** с информацией о коде и ошибке). У меня просто есть еще одна вещь, чтобы спросить, пожалуйста, извините мое невежество, если я что-то упустил .. при попытке заглушки заходит на book_obj.books [«Последний самурай»] Я просто пытаюсь присвоить значение ключу, поэтому я действительно не понимаю, что там int .. пожалуйста, объясните .. Спасибо. – boddhisattva

+0

Бесконечный цикл из-за цикла while в 'get_prices'. Вам нужно будет заглушить как плохой ввод, так и хороший ввод, чтобы пройти мимо этого, например. 'STDIN.stub (: получает) {" 40abc "}; STDIN.stub (: получает) {"40"}; '. –

+0

Синтаксическая ошибка заключается в том, что вы говорите: «STDOUT» должен получать строку, но она должна получать 'puts' _with_ a String, например. 'STDOUT.should_receive (: puts) .with (" this string ")'. То, что у вас есть, пытается найти метод с именем «Цена» не может быть 0 или отрицательным целым числом или в десятичном формате или буквенно-цифровом. \ NПожалуйста, введите соответствующую продолжительность в integer \ n ", таким образом, синтаксическую ошибку. –