Как я могу заглушить find_each для тестирования rspec в rails 3

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

MyClass.stub(:find).and_return(my_mock)

Однако в другом классе я делаю это:

MyClass.find_each do |instance_of_my_class|
  do_stuff_here_on(instance_of_my_class)
end

Я считаю, что если я сделаю это:

MyClass.stub(:find_each).and_return([one_mock, two_mock])

в тесте спецификации часть «сделать что-то здесь» не выполняется. Кто-нибудь знает, как заглушить find_each для тестирования rspec?


person Aaron    schedule 07.12.2010    source источник


Ответы (4)


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

MyClass.stub(:find_each).and_yield(one_mock).and_yield(two_mock)
person Iain    schedule 08.12.2010
comment
Это дает следующую ошибку: |мои макеты здесь| для блока с арностью 1. Если я передаю макеты в виде массива, у него нет проблемы, но instance_of_my_class в блоке find_each представляет собой массив, а не элементы в нем. - person Aaron; 08.12.2010
comment
Как насчет .and_yield(one_mock).and_yield(two_mock)? - person zetetic; 08.12.2010
comment
вы совершенно правы, zetetic - я обязательно проверю свой совет в следующий раз :) Я отредактировал свой ответ, включив ваше исправление. - person Iain; 09.12.2010
comment
Спасибо за исправление lain and zetetic! Это сработало правильно. Я только что получил книгу по rspec, но в Интернете мало полезной информации об API в целом. Есть ли у вас какие-либо источники, которые вы рекомендуете? - person Aaron; 09.12.2010
comment
Это хорошо сработало для имитации Find.find(/somepath) { block to test } - person Amir; 05.01.2011

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

Вот пример довольно высокого уровня:

class Example1
  def my_method
    # some logic
  end
end

class Example2
  def my_other_method
    Example1.find_each(&:my_method)
  end
end

Спецификация:

describe Example1 do
  it "should return something" do
    example = Example1.new
    example.my_method.should == something
  end
end

describe Example2 do
  it "should call my_method on Example1" do
    example1 = mock(:example1, :my_method => true)
    example2 = Example2.new

    example1.should_receive(:my_method)
    example2.my_other_method
  end
end
person Peter Brown    schedule 08.12.2010
comment
На самом деле я делаю логику в другом тестируемом методе. Я обновил блок find_each в своем вопросе, чтобы отразить это. Я больше пытаюсь проверить, выполняет ли он это на всех макетах, которые я передаю. - person Aaron; 08.12.2010

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

let(:my_relation_with_mocked_find_each) do
  relation = instance_double('YourModel::ActiveRecord_Relation')

  receive_yield = receive(:find_each)

  fake_objs.each do |obj|
    receive_yield = receive_yield.and_yield(obj)
  end

  allow(relation).to receive_yield
  relation
end
person dermotbrennan    schedule 01.10.2018

Это должно сделать это:

MyClass.stub(:find_each) {|block|
  block.call
  [one_mock, two_mock]
}

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

MyClass.stub(:find_each) {|block|
  some_object.instance_eval(&block)
  [one_mock, two_mock]
}
person Anders Kindberg    schedule 21.11.2011