Могу ли я FlexMock метод вызвать исключение при первом вызове, а затем вернуть действительный объект при втором вызове? - PullRequest
1 голос
/ 16 февраля 2011

Я работаю над кодом Ruby, который должен создавать первичные ключи в существующих таблицах базы данных MySQL. Он должен обнаруживать и исправлять дубликаты, чтобы обеспечить успешное создание индекса, используя следующий алгоритм:

 # 1. Query
 ALTER TABLE foo ADD PRIMARY KEY (id)
 # 2. Handle exception:
 Duplicate entry '3761290' for key 'PRIMARY' (Mysql::Error)
 # 3. Query:
 SELECT COUNT(1) FROM TABLE foo WHERE id = 3761290
 # 4. (Assuming 5 rows were returned from the previous query) Query:
 DELETE FROM TABLE foo WHERE id = 3761290 LIMIT 4 OFFSET 1
 # 5. retry ALTER TABLE query

Тест выглядит примерно так:

def test_create_primary_key
  table = 'foo'
  db = flexmock
  db.should_receive(:prepare).
      with("ALTER TABLE #{table} ADD PRIMARY KEY (id)").
      twice.
      and_raise(Mysql::Error, "Duplicate entry '3761290' for key 'PRIMARY'")
  db.should_receive(:prepare).
      with("SELECT COUNT(1) FROM #{table} WHERE id = ?").
      once.
      and_return(MockStatement.new [ [5] ])
  db.should_receive(:prepare).
      with("DELETE FROM #{table} WHERE id = ? LIMIT 4 OFFSET 1").
      once.
      and_return(MockStatement.new [ [5] ])

  indexer = Indexer.new :database_handle => db
  indexer.create_indexes table
end

Проблема заключается в том, что код будет выполняться в бесконечном цикле (если только он не имеет условия максимального повторения, что вполне может быть выполнено), поскольку он продолжит получать исключение из базы данных FlexMock.

В идеале макет должен быть в состоянии вызвать исключение в первый раз, а затем вернуть действительный дескриптор оператора во второй раз. Блочная форма #with может сработать, но я бы хотел сделать это чисто, если это возможно.

Есть идеи?

1 Ответ

1 голос
/ 28 февраля 2011

Я забыл, что поскольку Ruby (как правило) соответствует Принципу наименьшего удивления , нужно просто попробовать, что имеет смысл, и посмотреть, что произойдет:

  def test_yield_then_return
    mock = flexmock
    flexmock(mock).should_receive(:foo).
        with(:bar).
        and_raise(RuntimeError).
        and_return(true)

    assert_raises(RuntimeError) { mock.foo :bar }
    assert mock.foo(:bar)
  end

Делает то, что говорит на банке. :)

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