Я хочу использовать макеты RSpec, чтобы обеспечить постоянный ввод в блок.
Рубин:
class Parser
attr_accessor :extracted
def parse(fname)
File.open(fname).each do |line|
extracted = line if line =~ /^RCS file: (.*),v$/
end
end
end
RSpec:
describe Parser
before do
@parser = Parser.new
@lines = mock("lines")
@lines.stub!(:each)
File.stub!(:open).and_return(@lines)
end
it "should extract a filename into extracted" do
linetext = [ "RCS file: hello,v\n", "bla bla bla\n" ]
# HELP ME HERE ...
# the :each should be fed with 'linetext'
@lines.should_receive(:each)
@parser.should_receive('extracted=')
@parser.parse("somefile.txt")
end
end
Это способ проверить правильность работы внутреннего устройства блока, передав в него фиксированные данные. Но я не могу понять, как выполнить фактическую подачу с помощью механизма фиксации RSpec.
Обновить: похоже, что проблема была не в строчном тексте, а в:
@parser.should_receive('extracted=')
это не так, как он называется, замена его в коде ruby на self.extracted = немного помогает, но почему-то кажется неправильным.

У меня нет компьютера с доступными Ruby и RSpec, чтобы проверить это, но я подозреваю, что вам нужно добавить вызов к and_yields call [1] на конце should_receive(:each). Однако вам может быть проще не использовать mocks в этом случае, например. вы можете вернуть экземпляр StringIO, содержащий linetext, из заглушки File.open.
[1] http://rspec.rubyforge.org/rspec/1.1.11/classes/Spec/Mocks/BaseExpectation.src/M000104.html
and_yields не делает то, что мне нужно, а если и делает - тогда я просто не могу понять, как, черт возьми, это работает.
Нет, ты прав. Похоже, что моя ошибка на самом деле находится в @ parser.should_receive ('extract ='). это просто не правильно ... не работает.
Когда я заменил "extract = " на "self.extracted = " в коде Ruby, он начал работать правильно. Последние два дня гнался не за тем багом.
Я бы пошел с идеей заглушить вызов File.open
lines = "RCS file: hello,v\n", "bla bla bla\n"
File.stub!(:open).and_return(lines)
Этого должно быть достаточно для проверки кода внутри цикла.
Это должно помочь:
describe Parser
before do
@parser = Parser.new
end
it "should extract a filename into extracted" do
linetext = [ "RCS file: hello,v\n", "bla bla bla\n" ]
File.should_receive(:open).with("somefile.txt").and_return(linetext)
@parser.parse("somefile.txt")
@parser.extracted.should == "hello"
end
end
В классе Parser есть некоторые ошибки (он не проходит тест), но я бы написал тест именно так.
Чтобы конкретизировать, как работает «and_yield»: я не думаю, что «and_return» - это действительно то, что вам здесь нужно. Это установит возвращаемое значение блока File.open, а не строки, переданные его блоку. Чтобы немного изменить пример, скажите, что у вас есть это:
Рубин
def parse(fname)
lines = []
File.open(fname){ |line| lines << line*2 }
end
Rspec
describe Parser do
it 'should yield each line' do
File.stub(:open).and_yield('first').and_yield('second')
parse('nofile.txt').should eq(['firstfirst','secondsecond'])
end
end
Пройдешь. Если вы заменили эту строку на 'and_return', например
File.stub(:open).and_return(['first','second'])
Это не удастся, потому что блокировка обходится:
expected: ["firstfirst", "secondsecond"]
got: ["first", "second"]
Таким образом, нижняя строка - это использование and_yield для имитации ввода в блоки типа «каждый». Используйте and_return, чтобы имитировать вывод этих блоков.
Одна извлекаемая ошибка должна быть переменной экземпляра, @extracted. См. Мой комментарий для правильного теста.