Ruby on rails 如何使用FlexMock哈希匹配器验证数组值的一部分?
我试图验证我的Rails代码是否调用ActiveRecord的all方法(对于find:all,all是语法糖),如下所示:Ruby on rails 如何使用FlexMock哈希匹配器验证数组值的一部分?,ruby-on-rails,ruby,unit-testing,mocking,Ruby On Rails,Ruby,Unit Testing,Mocking,我试图验证我的Rails代码是否调用ActiveRecord的all方法(对于find:all,all是语法糖),如下所示: records = Record.all :limit => RECORD_LIMIT, :offset => record_offset, :select => 'id', :conditions => [ 'record_type = ? AND content_score >=
records = Record.all
:limit => RECORD_LIMIT, :offset => record_offset,
:select => 'id',
:conditions => [ 'record_type = ? AND content_score >= ?', 'user', min_content_score ],
:order => 'content_score DESC'
mock.should_receive(:all).with FlexMock.hsh [ 'record_type = ? AND content_score >= ?', Object, Object ]
mock.should_receive( :all ).
with( Flexmock.key( :conditions ) ).
and_return []
在这个实例中,我关心的代码的唯一部分是:conditions参数,我只关心SQL代码段,而不关心绑定变量的实际值。我可以使用FlexMock哈希匹配器断言(至少)存在:conditions参数,如下所示:
mock.should_receive(:all).with FlexMock.hsh :conditions => []
mock.should_receive(:all).
with( HashKeyMatcher.new( :conditions ) ).
and_return []
但是,这只匹配:conditions参数的值为空数组的调用。我真正想要的是这样的东西:
records = Record.all
:limit => RECORD_LIMIT, :offset => record_offset,
:select => 'id',
:conditions => [ 'record_type = ? AND content_score >= ?', 'user', min_content_score ],
:order => 'content_score DESC'
mock.should_receive(:all).with FlexMock.hsh [ 'record_type = ? AND content_score >= ?', Object, Object ]
mock.should_receive( :all ).
with( Flexmock.key( :conditions ) ).
and_return []
但不幸的是,正如irb所揭示的,“用户”和“对象”并不等同:
>> '' === Object
有什么好主意吗?嵌套匹配器是否可行?解决此问题需要猴子修补FlexMock和一个对象,因此不适合心脏虚弱的人。:) 首先,monkey patch FlexMock::HashMatcher在期望对象而不是实际对象上调用==:
class FlexMock::HashMatcher
def ===(target)
@hash.all? { |k, v| v == target[k] }
end
end
接下来,构造一个对象并重新定义其==方法:
conditions_matcher = Object.new
conditions_matcher.instance_eval do
def ==(other)
other.first == 'record_type = ? AND content_score >= ?'
end
end
最后,设置您的FlexMock期望值:
mock.should_receive(:all).
with(FlexMock.hsh(:conditions => conditions_matcher)).
and_return []
这适用于以下所有调用:
Record.all :conditions => [ 'record_type = ? AND content_score >= ?' ]
Record.all :conditions => [ 'record_type = ? AND content_score >= ?', 'foo' ]
Record.all :conditions => [ 'record_type = ? AND content_score >= ?', 'foo', 5.0 ]
鲁比是个疯子,但在某种程度上是好的 鸭子出击这应该不是必要的。您可以非常轻松地定义自己的参数匹配器:
class HashKeyMatcher
def initialize( key )
@key = key
end
def ===( other )
other.respond_to?( :keys ) && other.keys.include? @key
end
def inspect() "HashKeyMatcher( #{@key.inspect} )"; end
end
然后你就这样使用它:
mock.should_receive(:all).with FlexMock.hsh :conditions => []
mock.should_receive(:all).
with( HashKeyMatcher.new( :conditions ) ).
and_return []
将HashKeyMatcher放在可以从测试访问的地方
如果愿意,可以通过打开Flexmock类并添加新方法,以类似于其他Flexmock参数匹配器的方式使其可用:
class Flexmock
def self.key( hash_key )
HashKeyMatcher.new hash_key
end
end
然后您可以这样编写测试:
records = Record.all
:limit => RECORD_LIMIT, :offset => record_offset,
:select => 'id',
:conditions => [ 'record_type = ? AND content_score >= ?', 'user', min_content_score ],
:order => 'content_score DESC'
mock.should_receive(:all).with FlexMock.hsh [ 'record_type = ? AND content_score >= ?', Object, Object ]
mock.should_receive( :all ).
with( Flexmock.key( :conditions ) ).
and_return []
啊。。。源代码似乎表明我想要做的是不可能的:除非我可以重写right==方法。。。请继续关注。如果只将FlexMock::HashMatcher#===方法定义为“@hash.all?{k,v | v==target[k]}”,我对==的猴子补丁就可以了。也许是时候开公开课了。好吧,我比我疯狂的解决方案更喜欢这个。谢谢