我正在尝试验证我的Rails代码调用ActiveRecord的all方法(所有方法都是find:all的语法糖),如下所示:
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'
我在这个实例中关心的这段代码的唯一部分是:conditions param,我只关心SQL片段,而不是绑定变量的实际值。我可以使用FlexMock哈希匹配器断言(至少):条件param存在如下:
mock.should_receive(:all).with FlexMock.hsh :conditions => []
但是,这只匹配其中:conditions param的值为空数组的调用。我真正想要的是这样的:
mock.should_receive(:all).with FlexMock.hsh [ 'record_type = ? AND content_score >= ?', Object, Object ]
但悲惨的是,正如irb所揭示的那样,'用户'和对象并不等同:
>> '' === Object
有什么好主意吗?嵌套匹配器是否可能?
答案 0 :(得分:1)
鸭子打孔这不应该是必要的。您可以非常轻松地定义自己的参数匹配器:
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( HashKeyMatcher.new( :conditions ) ).
and_return []
将HashKeyMatcher放在您可以从测试中访问的位置。
如果您愿意,可以通过打开Flexmock类并添加新方法,以类似于其他Flexmock参数匹配器的方式使其可用:
class Flexmock
def self.key( hash_key )
HashKeyMatcher.new hash_key
end
end
然后你可以像这样编写你的测试:
mock.should_receive( :all ).
with( Flexmock.key( :conditions ) ).
and_return []
答案 1 :(得分:0)
对此的解决方案需要对FlexMock和对象进行猴子修补,因此不适合胆小的人。 :)
首先,猴子补丁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 ]
Ruby很疯狂,但是很好。 :)