首页 > 解决方案 > 在控制器测试中维护使用 FactoryBot 创建的 ActiveRecord 关联

问题描述

我正在尝试加快 Rails 控制器的一些测试,瓶颈与创建大量对象并持久化到数据库有关。我正试图用解决这个问题的电话来代替大部分create电话。build

运行Rails 5.1MiniTest 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, buyer: @buyer, 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包含我所有关联事务的对象,并且各个事务都附加了它们的关联项。但是,如果我将 abinding.pry放入实际完成工作的控制器方法中,并查看User我看到正确的对象,但user.transaction_checkout_items现在返回一个空Transaction::ActiveRecord_Associations_CollectionProxy对象,其中没有任何内容。本质上,关联消失了,这对我来说很有意义,因为控制器正在User从数据库中提取对象并对其进行处理,而这个新对象缺少关联。我考虑过尝试any_instance在类上存根一个方法,User以便无论何时#transaction_checkout_items被称为它返回Transaction对象的集合,但我看不到任何创建新::ActiveRecord_Associations_CollectionProxy对象的方法。我不能简单地为此使用数组或其他集合,因为::ActiveRecord_Associations_CollectionProxy需要调用控制器逻辑才能工作的方法。

所以我在星期五被封锁了。我transaction_checkout_items在任何User实例上存根的想法是一个好的想法,如果是这样,我该怎么做?或者是否有任何人可以建议的替代策略允许 MiniTest 存根关联在控制器代码运行时持续存在并可用?

标签: ruby-on-railsactiverecordfactory-botminitest

解决方案


我在任何用户实例上存根 transaction_checkout_items 的想法是一个好的想法,如果是这样,我该怎么做?

删除 ActiveRecord 方法几乎总是一个坏主意。它将您的测试与实现紧密耦合,并可能使更新 Rails / ActiveRecord 变得困难,就好像框架中的任何更改开始破坏您的测试一样。可能还有很多你没有想到的有趣的副作用。

问题也是你真正想要测试什么?如果您开始删除这些方法,您在测试什么?在控制器/集成测试中,我希望您想要测试以从数据库中获取正确的记录。

使用 build 与 create 来提高测试性能是一个很好的技巧,但正如您已经发现的那样,只有在测试中使用相同的对象时才有价值。不幸的是,这对于集成测试是不可能的,您需要/应该保留记录。

或者是否有任何人可以建议的替代策略允许 MiniTest 存根关联在控制器代码运行时持续存在并可用?

我会考虑为什么这很慢,如果这真的是一个问题。您的测试和整个测试套件运行多长时间,或者这只是过早的优化?

如果它变慢的原因仅仅是因为您需要创建大量测试数据,您可以使用固定装置或为您的数据库播种。尽管会带来不同的问题(例如MysteryGuest) ,但这都比使用 FactoryBot 更快


推荐阅读