ruby-on-rails - 在控制器测试中维护使用 FactoryBot 创建的 ActiveRecord 关联
问题描述
我正在尝试加快 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, 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 存根关联在控制器代码运行时持续存在并可用?
解决方案
我在任何用户实例上存根 transaction_checkout_items 的想法是一个好的想法,如果是这样,我该怎么做?
删除 ActiveRecord 方法几乎总是一个坏主意。它将您的测试与实现紧密耦合,并可能使更新 Rails / ActiveRecord 变得困难,就好像框架中的任何更改开始破坏您的测试一样。可能还有很多你没有想到的有趣的副作用。
问题也是你真正想要测试什么?如果您开始删除这些方法,您在测试什么?在控制器/集成测试中,我希望您想要测试以从数据库中获取正确的记录。
使用 build 与 create 来提高测试性能是一个很好的技巧,但正如您已经发现的那样,只有在测试中使用相同的对象时才有价值。不幸的是,这对于集成测试是不可能的,您需要/应该保留记录。
或者是否有任何人可以建议的替代策略允许 MiniTest 存根关联在控制器代码运行时持续存在并可用?
我会考虑为什么这很慢,如果这真的是一个问题。您的测试和整个测试套件运行多长时间,或者这只是过早的优化?
如果它变慢的原因仅仅是因为您需要创建大量测试数据,您可以使用固定装置或为您的数据库播种。尽管会带来不同的问题(例如MysteryGuest) ,但这都比使用 FactoryBot 更快
推荐阅读
- javascript - Ionic 中的多个构造函数打字稿:怎么做?
- go - 使用 Go 语法的函数组合
- image - React Native:模态屏幕未通过点击关闭
- rust - 使用带有 iter().map() 的函数 - 作为命名函数与作为闭包
- sql - 选择查询不能正常工作-SQL
- bash - 使用 bash 在 CSV 中随机更新列值
- reactjs - 从地图调用函数以返回总库存时做出反应(获取循环或无结果)
- react-native - 'navigationOptions' 中的弃用: - 'headerLeft:
' 将在未来的版本中删除。使用 'headerLeft: () => ' - java - Unable to rethrow Exception
- git - 从修改后的拉取请求中取回提交