这个warning(向下滚动到“嵌套事务”)指出函数
foo
:
def foo
User.transaction do
<stuff that mutates the database>
raise ActiveRecord::Rollback
end
end
将根据是否从事务块内部调用而表现不同。我知道这一点,我也知道 Rails 在事务中执行每个测试用例。
我有看起来像
foo
的功能,并且它们在测试中的表现符合预期。我的问题是:Rails 如何做到这一点?猴子补丁 ActiveRecord::Base#transaction
在测试中默认包含 requires_new: true
吗?
(我在 Postgres 上。)
结果比我想象的要简单:
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
# the big reveal ----------------------------------^^^^^^^^^^^^^^
def test_me
transaction joinable: false do # test transaction
transaction do # your transaction
Item.create!
raise ActiveRecord::Rollback
end
end
end
# just like a test log
#=>
# TRANSACTION (0.1ms) begin transaction
# TRANSACTION (0.0ms) SAVEPOINT active_record_1
# Item Create (0.3ms) INSERT INTO "items" DEFAULT VALUES
# TRANSACTION (0.1ms) ROLLBACK TO SAVEPOINT active_record_1
# TRANSACTION (9.7ms) commit transaction
和这个一样:
def test_me
transaction do
transaction requires_new: true do
Item.create!
raise ActiveRecord::Rollback
end
end
end
测试交易从这里开始:
https://github.com/rails/rails/blob/v7.0.4.3/activerecord/lib/active_record/test_fixtures.rb#L132
设置:
# app/models/item.rb
class Item < ApplicationRecord
def self.dont_make_item
transaction do
Item.create!
raise ActiveRecord::Rollback
end
end
def self.do_make_item
transaction do
transaction do
Item.create!
raise ActiveRecord::Rollback
end
end
end
end
# no surprises here
>> Item.dont_make_item
TRANSACTION (0.1ms) begin transaction
Item Create (0.4ms) INSERT INTO "items" DEFAULT VALUES
TRANSACTION (0.2ms) rollback transaction
# but this one commits the transaction
>> Item.do_make_item
TRANSACTION (0.1ms) begin transaction
Item Create (1.3ms) INSERT INTO "items" DEFAULT VALUES
TRANSACTION (9.5ms) commit transaction
在这里直接获取词汇:
# this is a transaction block
transaction do
end
# this is the actual database transaction
TRANSACTION (0.1ms) begin transaction
TRANSACTION (0.2ms) commit transaction
# notice how nested transaction block...
transaction do
transaction do
end
end
# ...doesn't make a second database transaction
TRANSACTION (0.1ms) begin transaction
TRANSACTION (9.5ms) commit transaction
# the only thing nested transaction block does is catch the error
raise ActiveRecord::Rollback
# but there is no actual database transaction for that block to rollback
# so nothing happens
但在测试中
dont_make_item
被包裹在一个事务中两次,但仍然按预期工作。
# test/models/item_test.rb
require "test_helper"
class ItemTest < ActiveSupport::TestCase
test "dont make item" do
Item.dont_make_item
assert_equal 0, Item.count
end
end
TRANSACTION (0.1ms) begin transaction
------------------------------
ItemTest: test_dont_make_item
------------------------------
TRANSACTION (0.1ms) SAVEPOINT active_record_1
Item Create (0.2ms) INSERT INTO "items" DEFAULT VALUES
TRANSACTION (0.1ms) ROLLBACK TO SAVEPOINT active_record_1
Item Count (0.1ms) SELECT COUNT(*) FROM "items"
TRANSACTION (0.1ms) rollback transaction
SAVEPOINT active_record_1
是当您为嵌套事务块指定 requires_new: true
选项时发生的情况。除了我们的交易没有requires_new:
选项。 Rails 魔法在顶部解释 ^.