[Cucumber] Ruby常量跨Scenario异常留存问题咨询
这个问题的核心在于Ruby常量的本质和哈希浅复制的特性,再结合Cucumber的运行机制,导致了跨场景的数据留存。我们一步步拆解:
1. 为什么用常量会出现数据留存?
首先,Ruby中的常量(比如Constants::Cons)是全局单例对象——程序启动时就会初始化,并且在整个Ruby进程的生命周期中持续存在。Cucumber运行多个场景时,不会重启Ruby进程,所以常量的状态会在场景间保留。
再看你的代码:
temp = Constants::Cons.dup table.hashes[0].each do |key, values| temp[:bodyData][eval(":#{key}")] = values end
这里的dup是浅复制:它会创建一个新的外层哈希对象,但内层的bodyData哈希并没有被复制——temp[:bodyData]和Constants::Cons[:bodyData]指向的是同一个内存中的哈希对象。
所以第一个场景运行时,你修改的其实是Constants::Cons[:bodyData]这个共享的哈希。第二个场景启动时,Constants::Cons[:bodyData]已经有第一个场景的数据了,即使你dup外层哈希,内层还是那个被修改过的对象,自然会带上之前的数据。
你可以用一个简单的Ruby代码验证这个浅复制的问题:
a = { toService: "test", bodyData: {} } b = a.dup b[:bodyData][:user_id] = "king" puts a[:bodyData] # 输出 {:user_id=>"king"},说明a的bodyData被修改了
另外,Ruby其实允许修改常量的内容(虽然会抛出警告),这也是一个容易踩坑的点——你以为在操作副本,但实际上修改了全局的常量状态。
2. 为什么用方法就能正常工作?
当你改成用方法返回哈希时:
def self.cons { :toService => "Microcontrol", :bodyData=>{} } end
每次调用Constants.cons时,都会创建一个全新的哈希对象——包括内层的bodyData也是一个新的空哈希。每个场景拿到的都是独立的、干净的初始状态,修改的只是当前场景自己的哈希,完全不会影响其他场景。
这种方式相当于每次场景都初始化一个全新的配置对象,自然就不会有跨场景的数据留存问题。
3. 其他可行的解决方案
如果你一定要用常量,也可以通过深复制来解决浅复制的问题,确保内层哈希也被复制:
# 需要引入active_support(Rails框架自带,或者单独安装gem) temp = Constants::Cons.deep_dup
或者手动实现深复制逻辑,但这种方式不如用方法直观,而且常量本来就不应该被修改,违背了常量的设计初衷。
综上,用方法返回全新哈希的方式是最合理的——既符合Ruby的设计规范,也能避免Cucumber场景间的状态污染。
内容的提问来源于stack exchange,提问作者Plato

