FactoryBot定义无法识别Lambda?Ruby/Rspec技术问询
问题分析与解决
首先,咱们来看你代码里的核心问题:在FactoryBot的transient块中,你用局部变量赋值的方式定义了Lambda,但这并不是FactoryBot识别transient属性的正确方式。
你原来的写法:
transient do remove_used_value = -> (x,y) { x.reject { |a| a == y }.sample } end
这里的remove_used_value只是transient块内的局部变量,并没有被注册为FactoryBot的transient属性,所以当你在后面调用remove_used_value.call时,FactoryBot的SyntaxRunner实例根本找不到这个方法,就会抛出undefined method的错误。之前偶然能运行可能是因为局部变量的作用域巧合被保留了,但这并不是正确的用法。
修正后的正确写法
把Lambda用FactoryBot的transient DSL来定义(用块的方式),这样它才会成为可被调用的transient属性:
contact_methods = %w[Phone Email Snail\ Mail Carrier\ Pigeon] factory :my_object do transient do remove_used_value { ->(x, y) { x.reject { |a| a == y }.sample } } end first_contact_option { contact_methods.sample } second_contact_option { remove_used_value.call(contact_methods, first_contact_option) } end
更推荐的实现方式
如果多个工厂都需要用到这个“移除已用值并随机取样”的逻辑,更优雅的做法是把它提取成全局辅助方法,而不是放在transient里:
- 在
spec/support目录下新建一个辅助文件(比如factory_bot_helpers.rb):
module FactoryBotHelpers def remove_used_value(collection, used_item) collection.reject { |item| item == used_item }.sample end end # 让FactoryBot的SyntaxRunner包含这个模块,这样所有工厂都能调用这个方法 FactoryBot::SyntaxRunner.include(FactoryBotHelpers)
- 然后在你的工厂里直接使用这个方法,代码会更简洁:
contact_methods = %w[Phone Email Snail\ Mail Carrier\ Pigeon] factory :my_object do first_contact_option { contact_methods.sample } second_contact_option { remove_used_value(contact_methods, first_contact_option) } end
这种方式不仅可读性更高,而且复用性更强,后续其他工厂需要相同逻辑时直接调用方法即可,不需要重复定义Lambda。
内容的提问来源于stack exchange,提问作者Carl
相关产品推荐
相关产品推荐

