问题描述
我正在尝试加快 Rails 控制器的一些测试,瓶颈与创建大量对象并将其持久化到数据库有关。我正在尝试将大部分 create
调用替换为 build
调用以解决此问题。
运行 Rails 5.1
,并使用 MiniTest 5.10.3
和 FactoryBot 5.0.2
。
我正在尝试从这里开始
@user = create(:user)
@item1 = create(:item)
@item1 = create(:item)
@transaction1 = create(:transaction,buyer: @buyer,item: @item1)
@transaction2 = create(:transaction,item: @item2)
在此应用程序中,item
表示可销售对象,user
表示购买者,而 transaction
是创建两者的对象。 User
类还添加了一个关联 transaction_checkout_items
,它返回处于可以完成购买状态的所有 Transaction
项。
因此,在每次测试中,我们都会创建无数对象并将它们全部保存到数据库中。它很慢,但它有效。不过,我希望它更快,所以我尝试用这样的东西替换现有的设置:
@user = create(:user)
def build_transaction_checkout_items(user,item)
user.transaction_checkout_items.build(attributes_for(:transaction,buyer: user,sale_price: item.sale_price,item: item))
end
@item1 = build_stubbed(:item)
@item2 = build_stubbed(:item)
@transaction1 = build_transaction_checkout_items(@buyer,@item1)
@transaction2 = build_transaction_checkout_items(@buyer,@item2)
只要我在测试中,这似乎就可以工作。如果我在测试中删除绑定并检查对象 @user
返回用户对象,@user.transaction_checkout_items
返回包含我所有关联交易的 Transaction::ActiveRecord_Associations_CollectionProxy
对象,并且各个交易都附加了它们的关联项目.但是,如果我将 binding.pry
放入实际完成工作的控制器方法中,并查看 User
对象,我会看到正确的对象,但是 user.transaction_checkout_items
现在返回一个空的 {{1 }} 没有任何内容的对象。基本上关联消失了,这对我来说很有意义,因为控制器从数据库中提取 Transaction::ActiveRecord_Associations_CollectionProxy
对象并开始处理它,而这个新对象缺少关联。我曾考虑尝试在 User
类上存根 any_instance
方法,以便每当调用 User
时它都会返回 #transaction_checkout_items
对象的集合,但我没有看到以任何方式创建新的 Transaction
对象。我不能简单地为此使用数组或其他集合,因为 ::ActiveRecord_Associations_CollectionProxy
上有一些方法需要调用才能使控制器逻辑工作。
所以我在星期五被封锁了。我在任何 ::ActiveRecord_Associations_CollectionProxy
实例上存根 transaction_checkout_items
的想法是否是一个好主意,如果是这样,我该怎么做?或者是否有任何人可以建议的替代策略,允许 MiniTest 存根关联在控制器代码运行时持续存在并可用?
解决方法
我在任何 User 实例上存根 transaction_checkout_items 的想法是否可行,如果是,我该怎么做?
剔除 ActiveRecord 方法几乎总是一个坏主意。它会将您的测试与实现紧密耦合,并且可能会使更新 Rails / ActiveRecord 变得困难,就好像您的测试开始破坏框架中的任何更改一样。可能还有很多你没有想到的有趣的副作用。
问题也是你真正想测试什么?如果你开始排除这些方法,你在测试什么?在控制器/集成测试中,我希望您希望测试从数据库中获取正确的记录。
使用构建与创建来提高测试性能是一个很好的技巧,但正如您已经发现的那样,只有在测试中使用相同的对象时才有价值。不幸的是,这对于集成测试是不可能的,您需要/应该保留记录。
或者是否有任何人可以建议的替代策略,允许 MiniTest 存根关联在控制器代码运行时持续存在并可用?
我会考虑为什么这很慢,以及这是否真的是一个问题。您的测试和整个测试套件运行了多长时间,或者这只是过早的优化?
如果它缓慢的原因仅仅是因为您需要创建大量测试数据,您可以使用夹具或为数据库设置种子。尽管会带来不同的问题(例如 MysteryGuest)
,但这都比使用 FactoryBot 更快