Как протестировать программу командной строки Ruby с помощью Cucumber, которая выводит огромное количество данных?

Я создаю программу командной строки Ruby и использую Cucumber и Aruba для ее тестирования. Aruba включает в себя несколько очень удобных сопоставителей, поэтому я могу проверить вывод с помощью нескольких строк в файле .feature:

When I run `myprogram`
Then it should pass with:
  """
  my program output
  """

Проблема в том, что моя программа может содержать десятки или даже сотни строк вывода; размещение всего этого в файле .feature затруднит чтение и навигацию (и это немного неприятно). Каков рекомендуемый способ проверки вывода в таком случае?


person mipadi    schedule 04.12.2012    source источник
comment
Почему ты задал этот вопрос дважды? stackoverflow.com/questions/13713162/   -  person simonmorley    schedule 05.12.2012
comment
@simonmorley: Потому что это разные проблемы…?   -  person mipadi    schedule 05.12.2012
comment
Хорошо, было довольно странно видеть обоих рядом друг с другом. С   -  person simonmorley    schedule 05.12.2012


Ответы (4)


Короткий ответ: вы не должны этого делать.

Огуречные тесты должны быть ориентированы на пользователя и читабельны. Они описывают особенности. Пользователю все равно, соответствует ли вывод ошибки некоторому известному значению байт за байтом.

Вы должны спросить себя: что я тестирую? Является ли ответ сообщением об ошибке? Возможно нет. Вы тестируете некоторые функции в своем приложении. Если все, что вы действительно хотите гарантировать, что это не сработает, то в сценарии Cucumber вам нужна следующая строка:

Then the exit status should not be 0

Это предполагает, что сценарий следует стандартному соглашению о том, что ненулевой статус выхода сигнализирует об ошибке.

Если ваш сценарий требует, чтобы на выходе было определенное сообщение, вы можете добавить его:

Then it should fail with
"""
Some error message
"""

но это не обязательно должен быть полный вывод, а только частичное совпадение. (Обратите внимание, что в aruba определено «это должно завершиться ошибкой точно:», но я не рекомендую его использовать.)

Редактировать: вы изменили свой пример, чтобы он тестировал успешно, а не неудачно, но мой основной совет остается прежним:

  1. Отделяйте специфику вывода от логики сценария. Используя пример в комментариях, если у вас есть тест, который подтверждает, что вы можете вывести один пользовательский комментарий, и другой тест, который подтверждает, что вы вывели правильные 100 комментариев, то этого достаточно, вы не нужно иметь вывод на 100 комментариев в сценарии Cucumber.
  2. Пишите сценарии Cucumber с точки зрения пользователя. Каждый сценарий должен тестировать что-то важное для пользователя. Старайтесь свести их к минимуму, удаляя все, что просачивается из реализации, когда пользователю все равно.
  3. Для этого используйте встроенные конструкции Aruba, которые проверяют частичные совпадения. Ищите ключевые слова или фразы в выводе. Мало того, что тесты Cucumber будут легче читать, они будут более надежными и невосприимчивыми к несвязанным изменениям вывода.
person Mark Thomas    schedule 11.12.2012
comment
Как проверить, действительно ли программа выводит что-то полезное? Он может завершиться с 0, фактически не выполняя никакой реальной работы или не выводя ничего значимого. Или это то, что не нужно проверять? - person mipadi; 12.12.2012
comment
В некоторой степени, как я могу проверить, что, например, разные флаги, переданные программе, печатают вывод, который они должны выводить. Или это не подходит для проверки? - person mipadi; 12.12.2012
comment
@mipadi Я попытался ответить на эти вопросы в редактировании своего поста. - person Andrei Botalov; 12.12.2012
comment
@mipadi Просто проверьте ту часть сообщения, которая уникальна. Вам не нужно вводить полное сообщение. - person Mark Thomas; 13.12.2012
comment
@MarkThomas: Ну, в некотором смысле весь вывод уникален; это список пользовательских комментариев. Но не могли бы вы предложить просто протестировать первые несколько строк вывода? У меня есть уже есть тест RSpec, который гарантирует, что я генерирую правильное количество комментариев для листинга (в данном случае 100). (Я думаю, единственное, что мне интересно, это то, будет ли изменение теста Cucumber таким образом действительно проверять, вывожу ли я правильное количество комментариев… но, возможно, это не то, что я должен или должен тестировать ?) - person mipadi; 14.12.2012
comment
В вашем примере кода говорилось о сценарии сбоя. Вы хотите, чтобы пользовательские комментарии для этого? Если вы говорите о другом сценарии, это другая история. Не зная подробностей, трудно сказать что-то конкретное, но если у вас есть тест, который подтверждает, что вы можете правильно вывести один комментарий, а другой подтверждает, что вы выводите ровно 100 комментариев, то я бы сказал, что нет. Не засоряйте сценарии Cucumber текстом на 100 комментариев. - person Mark Thomas; 14.12.2012
comment
@MarkThomas: Это был только один пример. Существует множество сценариев, некоторые неудачные тесты и некоторые успешные тесты (я отредактировал свой вопрос, чтобы сделать это более ясным). - person mipadi; 15.12.2012

Для тестирования вашей программы у вас есть 2 варианта:

  1. Проверяйте только соответствующую часть сообщения (то, что вы действительно хотите проверить с помощью этого теста). В Aruba есть встроенный stepdef. так что это очень легко

  2. Проверьте полное сообщение. Если сообщение короткое, вы можете использовать встроенную функцию Aruba. шаг определения. Однако, если сообщение длинное, вы можете поместить его в отдельный файл. Так как Aruba не содержит такого метода, вы должны написать этот stepdef самостоятельно.

Это может выглядеть так:

# Require aruba/api before that
Then /^it should (pass|fail) with message from file "(.*)"$/ do |pass_fail, filename|
  exact_output = File.read(filename)
  Aruba::API::assert_exit_status_and_output(pass_fail == "pass", exact_output, true)
end

Если вы хотите написать много тестов и много похожих сообщений, у вас может получиться много WET. Утверждение полного сообщения усложнит вспомогательные тесты в случае изменений.

Таким образом, вы можете захотеть использовать какой-то механизм шаблонов для подтверждения этих сообщений, чтобы сделать тесты DRYer.

Вы можете спроектировать свои тесты по-другому.
Если вы используете только первый метод, ваши тесты могут не найти некоторые регрессионные ошибки, поскольку они не тестируют все. Если вы будете использовать второй метод, то поддерживать их может быть намного сложнее, если между сообщениями много повторов.

Таким образом, существует компромисс между этими двумя вариантами. Обычно вы строите одни тесты по методу 1, другие по методу 2. Вы должны подумать, что будет лучше в вашем случае. Я не знаю никакого золотого правила.

person Andrei Botalov    schedule 11.12.2012

Сотни выходных данных неудивительны в приложении. И да, вам нужно сделать их легко читаемыми. Если вы не можете сделать это в файлах функций Cucumber, боюсь, лучшего места для замены не найти. В конце концов, Cucumber написан на простом английском и действует как живая документация.

Все, что вам нужно, это организовать их. Вот несколько основных советов на случай, если вы ими пренебрегли.

  1. Напишите конкретную функцию, охватывающую одну тему, только в .feature файле. Хотя я знаю, что вы упомянули When I run my program для демонстрационных целей, такая широкая тема неприемлема.

  2. Поместите .feature файлов в папку features/. И, при необходимости, дополнительно разбить их на подпапки типа features/user/user_login.feature.

  3. Используйте Scenario Outline и Examples для организации похожих выходных данных в одном сценарии. Примеры будут иметь ряды для четкого представления каждого из них.

Надеюсь, это поможет.

person Billy Chan    schedule 11.12.2012

Я бы рекомендовал следующее:

When I run `my_app`
Then the exit status should not be 0
And the output should contain:
"""
some output
"""

Таким образом, пока из вашей программы выводится «некоторый вывод», тест проходит успешно, но дополнительный вывод игнорируется, и вашему тесту не требуется весь этот вывод.

Если вы хотите утверждать все это за один шаг, просто сделайте свой собственный, хотя я думаю, что так понятнее.

person davetron5000    schedule 15.12.2012