Как проверить стандартный вывод внутри блока итерации .each с помощью rspec?

У меня есть пример кода:

  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, пожалуйста, поместите его туда.

spike 王建 10.12.2020 03:59

Извините, initialize_program на самом деле должен быть методом print_stuff, описанным выше. Но я решил это. Я просто поймал STDOUT с консоли, преобразовал в строку и проверил это. Спасибо хоть,

ninja_nugget 10.12.2020 08:40

вам нужно передать блок expect, если вы хотите использовать output сопоставитель, поэтому я думаю, что изменение на expect { print_stuff(1, 3) }.to output(...) должно сработать для вас (обратите внимание на фигурные скобки)

Stefan Rendevski 10.12.2020 10:03

Связанный: stackoverflow.com/q/65135207/1301972

Todd A. Jacobs 10.12.2020 17:26

@StefanRendevski Сопоставитель output_to_stdout будет работать, но в данном случае это похоже на анти-шаблон. Рефакторинг метода, чтобы его было легче тестировать, а не синтаксический анализ стандартного вывода, кажется более чистым подходом. Тем не менее, матчер, безусловно, является жизнеспособным вариантом.

Todd A. Jacobs 10.12.2020 17:49
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
1
5
505
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Рефакторинг для проверки строки, а не стандартного вывода

Этот тип кода — вот почему вы должны сначала написать свои тесты. По сути, вы тестируете 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

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

Тестирование примеров RSpec в IRB

В 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). Итак, что делает сопоставитель:

  1. сохраняет блок на потом
  2. это волшебство, чтобы захватить все, что идет в STDOUT (см. ниже)
  3. вызывает блок, вызывает initialize_program(1, 3), записывает все, что должно было попасть в STDOUT
  4. сравнивает это с тем, что вы настроили в своем ожидании (часть output("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.

Другие вопросы по теме