тестирование многоэтапного рабочего процесса в rspec

Я хотел бы узнать об идиомах или рекомендациях по тестированию многоэтапного рабочего процесса с использованием rspec.

Возьмем в качестве примера систему «корзина для покупок», где процесс покупки может быть

  1. когда пользователь отправляет в корзину, и мы не используем https, перенаправляем на https
  2. когда пользователь отправляет в корзину, и мы используем https и нет файла cookie, создайте и отобразите новую корзину и отправьте обратно файл cookie
  3. когда пользователь отправляет в корзину, и мы используем https, и есть действительный файл cookie, а новый элемент предназначен для продукта, отличного от первого элемента, добавьте строку в корзину и отобразите обе строки
  4. когда пользователь отправляет в корзину, и мы используем https, и есть действительный файл cookie, и новый элемент предназначен для того же продукта, что и предыдущий, увеличьте количество этой строки корзины и отобразите обе строки
  5. когда пользователь нажимает «оформить заказ» на странице корзины и использует https, и есть файл cookie, а корзина не пуста и...
  6. ...

Я прочитал http://eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/, который советует, в частности, что каждый «it-блок» должен содержать только одно утверждение: вместо выполнения вычислений и последующего тестирования нескольких атрибутов в одном и том же блок, используйте «до» внутри контекста, чтобы создать (или получить) тестируемый объект и назначить его @some_instance_variable, затем запишите каждый тест атрибута как отдельный блок. Это немного помогает, но в случае, описанном выше, когда шаг тестирования n требует выполнения всей настройки для шагов [1..n-1], я обнаруживаю, что либо дублирую код настройки (очевидно, не очень хорошо), либо создаю множество вспомогательных функций. со все более громоздкими именами (def create_basket_with_three_lines_and_two_products) и вызывая их последовательно в блоке перед каждым шагом.

Любые советы о том, как сделать это менее подробно/утомительно? Я ценю общий принцип, лежащий в основе идеи о том, что каждый пример не должен зависеть от состояния, оставленного предыдущими примерами, но когда вы тестируете многоэтапный процесс и что-то может пойти не так на любом этапе, настройка контекста для каждого шага неизбежно потребуется повторный запуск всех настроек для предыдущих n шагов, так что...


person telent    schedule 13.12.2010    source источник


Ответы (2)


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

class MultiStep
  def initialize(context)
    @context = context
  end

  def init_vars
    @cut = @context.instance_variable_get(:@cut)
  end

  def setup(step)
    init_vars
    method(step).call
  end

  def step1
    @cut.stub(:foo).and_return("bar")
  end

  def step2
    step1
    @cut.stub(:foo_bar).and_return("baz_baz")
  end
end

class Cut  # Class Under Test
  def foo
    "foo"
  end
  def foo_bar
    "foo_bar"
  end
end

describe "multiple steps" do
  before(:each) do
    @multi_stepper = MultiStep.new(self)
    @cut = Cut.new
  end

  it "should setup step1" do
    @multi_stepper.setup(:step1)
    @cut.foo.should == "bar"
    @cut.foo_bar.should == "foo_bar"
  end

  it "should setup step2" do
    @multi_stepper.setup(:step2)
    @cut.foo.should == "bar"
    @cut.foo_bar.should == "baz_baz"
  end

end
person zetetic    schedule 13.12.2010

Конечно, слишком поздно для OP, но это может быть удобно для других — гем rspec-steps, похоже, создан именно для этой ситуации: https://github.com/LRDesign/rspec-steps

Возможно, стоит взглянуть на https://github.com/railsware/rspec-example_steps и https://github.com/jimweirich/rspec-given. Я остановился на rspec-steps, но я торопился, и эти другие варианты могли бы быть лучше, насколько я знаю.

person Nerdmaster    schedule 27.03.2013
comment
теперь, когда вы использовали гем rspec-steps, довольны ли вы результатом решения проблемы многоэтапного уважения? - person Satchel; 11.05.2015
comment
@Анжела: Хм, хороший вопрос. Я использовал его ненадолго, но остальной группе он не понравился, так что в итоге мы отказались от него и стали больше дублировать. Думаю, причина заключалась в том, что нам нужно было больше читабельности, но это было ДОЛГОЕ время... На данный момент я считаю, что настоящие интеграционные тесты проще всего просто написать в виде больших функций, выполняющих единый рабочий процесс. за тест. Не самый идиоматический или популярный подход, но мне нравится читабельность. - person Nerdmaster; 11.05.2015
comment
Я вижу только один describe в rspec? - person Satchel; 13.05.2015
comment
Это будет зависеть, я думаю. Но в случае только спецификации последовательной интеграции, вероятно, будет одно описание, а затем несколько относительно больших тестовых блоков с несколькими ожиданиями. Тем не менее, это своего рода процесс, который все еще находится в стадии эволюции. Мы просто не определились с хорошим способом заставить спецификации интеграции работать так, как мы хотим. Мы стремились сделать очень сильные модульные тесты и дополнить их несколько разреженными/простыми спецификациями интеграции. Спросите меня еще раз через год, и это, вероятно, изменится :) - person Nerdmaster; 13.05.2015