我正在研究一些需要在现有MySQL数据库表上创建主键的Ruby代码。它需要检测并修复重复项以允许索引创建成功,使用以下算法:
# 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'd db中获得异常。
理想情况下,mock应该能够在第一次引发异常,然后第二次返回有效的语句句柄。 #with的块形式可能在这里起作用,但如果可能的话,我想以干净的方式进行。
有什么想法吗?
答案 0 :(得分:1)
我忘记了,因为Ruby(通常)符合The Principle of Least Astonishment,所以应该尝试一下有意义的事情并看看会发生什么:
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
它在锡上说的是什么。 :)