У меня есть пример кода:
def print_stuff(first_num, second_num)
puts 'hello'
(first_num..second_num).to_a.each do |num|
puts 'The current number is: '
puts "#{num}"
end
end
Я и используя rspec, я хотел бы проверить правильность вывода или нет. Моя попытка заключается в следующем:
expect(initialize_program(1, 3)).to output(
"The current number is:
1
The current number is:
2
The current number is:
3").to_stdout
Но вместо этого я получаю ожидаемый блок для вывода на стандартный вывод, но не ошибку блока, поскольку initialize_program(1,3) выводит тексты, но это делается внутри блока .each, поэтому сам метод возвращает массив диапазона чисел .
Как я могу проверить тексты внутри блока, чтобы убедиться, что выведенные тексты верны?
Спасибо!
Извините, initialize_program на самом деле должен быть методом print_stuff, описанным выше. Но я решил это. Я просто поймал STDOUT с консоли, преобразовал в строку и проверил это. Спасибо хоть,
вам нужно передать блок expect
, если вы хотите использовать output
сопоставитель, поэтому я думаю, что изменение на expect { print_stuff(1, 3) }.to output(...)
должно сработать для вас (обратите внимание на фигурные скобки)
Связанный: stackoverflow.com/q/65135207/1301972
@StefanRendevski Сопоставитель output_to_stdout будет работать, но в данном случае это похоже на анти-шаблон. Рефакторинг метода, чтобы его было легче тестировать, а не синтаксический анализ стандартного вывода, кажется более чистым подходом. Тем не менее, матчер, безусловно, является жизнеспособным вариантом.
Этот тип кода — вот почему вы должны сначала написать свои тесты. По сути, вы тестируете Kernel#puts, который всегда возвращает nil
, а не проверяете, создали ли вы ожидаемую строку. Не делай этого. Вместо этого выполните рефакторинг следующим образом:
def print_stuff(num1, num2)
str =
(num1..num2).map { |num|"The current number is: #{num}" }
.join "\n"
puts str
str
end
print_stuff 1, 3
#=> "The current number is: 1\nThe current number is: 2\nThe current number is: 3"
Это не только напечатает то, что вы ожидаете на стандартном выходе:
The current number is: 1 The current number is: 2 The current number is: 3
но также будет использовать неявный возврат последней строки вашего метода, чтобы вернуть значение, которое вы можете использовать для сравнения с ожиданиями в вашей спецификации.
Вы также можете реорганизовать метод, чтобы он возвращал объекты типа Array of String или что-то еще, что вы можете явно захотеть протестировать. Чем больше ваш реальный метод отражает то, что вы планируете тестировать, тем лучше.
RSpec.describe '#print_stuff' do
it 'prints the expected message' do
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
expect(print_stuff 1, 3).to eql(expected_string.chomp)
end
# Even without the collection matchers removed in RSpec 3,
# you can still validate the number of items returned.
it 'returns the expected number of lines' do
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)
end
end
В irb вы можете проверить свои спецификации следующим образом:
require 'rspec'
include RSpec::Matchers
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
# String#chomp is needed to strip the newline from the
# here-document
expect(print_stuff 1, 3).to eql(expected_string.chomp)
# test the returned object in other ways, if you want
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)
Ответ Тодда в порядке, и я настоятельно рекомендую вам внимательно его прочитать: реорганизуйте свое приложение таким образом, чтобы пользовательский интерфейс (CLI в вашем случае) был минимальным и легко тестируемым. Но когда вам нужен полный охват, вам нужно будет в конечном итоге протестировать этот стандартный вывод.
То, как вы используете это сейчас:
expect(initialize_program(1, 3)).to output("whatever").to_stdout
Означает, что initialize_program(1, 3)
оценивается немедленно, когда вызывается сопоставитель, и это слишком рано - он должен сначала сделать свою магию (*), а затем запустить ваш код. Попробуйте так:
expect { initialize_program(1, 3) }.to output("whatever").to_stdout
Теперь вместо того, чтобы передавать результаты вызова initialize_program(1, 3)
в сопоставитель, вы передаете блок, который «знает, как» вызывать initialize_program(1, 3)
. Итак, что делает сопоставитель:
initialize_program(1, 3)
, записывает все, что должно было попасть в STDOUToutput("whatever")
)https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L49 и https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L141
Он просто создает StringIO и заменяет им глобальную переменную $stdout.
где ваш метод
initialize_program
, пожалуйста, поместите его туда.