Rails 如何在将每个测试用例包装在事务中的同时正确测试使用事务的代码?

问题描述 投票:0回答:1

这个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 上。)

ruby-on-rails postgresql activerecord transactions
1个回答
0
投票

结果比我想象的要简单:

def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
# the big reveal ----------------------------------^^^^^^^^^^^^^^

https://github.com/rails/rails/blob/v7.0.4.3/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L309

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 魔法在顶部解释 ^.

© www.soinside.com 2019 - 2024. All rights reserved.