我一直在努力诊断仅在我的主分支上发生的测试失败。 以下是简化形式的相关代码。
有一项服务:
class Service
attr_reader :traces
def initialize
@traces = []
end
def do_work
@traces << Thread.current.backtrace
# ... actual work ...
end
end
以及使用该服务的类:
class Widget
def get_cached_service
puts("Getting a cached service!")
puts("Do I already have one? #{!!@service}")
@service ||= make_service
end
def make_service
puts("Making a service!")
Service.new
end
end
我有一个测试(存在于文件widget_spec.rb
中)间歇性失败。 此测试创建Widget
的实例并调用get_cached_service
。 我在控制台上看到Getting a cached service!
消息,后跟Do I already have one? false
,但我看不到Making a service!
消息。
此外,当我检查返回的Service
对象的traces
属性时,我发现源自项目中其他测试的堆栈跟踪(例如。foo_spec.rb
、bar_spec.rb
等)。
在几个不同的地方,我找到了这样的代码:
allow_any_instance_of(Widget)
.to receive(:make_service).and_return(whatever)
我发现其堆栈跟踪的其他测试可能会像这样make_service
。 但似乎在这些测试之后,存根并没有被撤消,根据我的理解,这应该总是发生。
除了 rspec 中的错误之外,是否有任何原因可能导致存根在测试结束时无法重置?
存根几乎肯定已被清除,但您已将假实例缓存在get_cached_service
中。没有什么可以清除@service
中的缓存值,RSpec(理所当然地)不知道它。因此,如果测试调用get_cached_service
,则存根make_service
是不够的。您有以下几种选择:
- 始终存根
get_cached_service
而不是make_service
,或者除了 - 提供一种方法来清除每次测试后调用的缓存值。
- 使缓存以某种方式可配置,或围绕实际实现的包装器,以便缓存不会在测试代码中发生。
我意识到这回答已经很晚了,但对于任何读到这篇文章的人来说:
使用 rspec 平分来确定是否存在导致失败的一致测试顺序,然后开始撕掉代码,直到只剩下中断的位。
我不记得 RSpec 有故障的情况 - 几乎总是,某处有一个类变量未被清除,或者有人正在手动玩一个类,比如define_method
.偶尔它可能发生在宝石中。
确保在spec_helper的每次测试后清除所有内容 - 清除Rails缓存,清除ActionMailer交付,从Timecop冻结返回等。
任何与RSpec直接相关的内容都应该在理论上清除自己,因为它旨在集成到RSpec中,并且可能是一般情况下最不可能的解释。