我需要类似于as_null_object的东西,它可以响应任何未明确设置的方法并返回nil或false。我不能使用as_null_object,因为它在调用未定义的方法时返回一个模拟对象(真正的等价物)。
我的用例是测试下面的cancan的ability.rb模型:
if user.admin?
# set abilities
end
#...many other if statements with respect to the different roles...
if user.publisher?
# set abilities
end
在这种情况下,用户有许多布尔角色方法。在我的测试中,我想通过创建一个只有一个角色的模拟用户对象来孤立地测试一个角色,而不必删除所有其他角色。如果没有定义角色方法,我希望它返回nil或false,因此它不会设置其他能力。类似的东西:
user = mock("User", :publisher? => true).all_other_methods_to_return(false)
参考:http://apidock.com/rspec/Spec/Mocks/Methods/as_null_object
答案 0 :(得分:1)
我认为你的方法不是最好的。我通常认为“白名单”方法比“黑名单”更好。所以我会做类似以下的事情:
def mock_user_with_role(role)
roles = %w(admin publisher) #add the rest of the roles here or do something like User::Roles
stubbed_methods = Hash[(roles - [role]).map {|r| ["#{r}?", false]}]
double("User", stubbed_methods.merge("#{role}?" => true))
end
上面的方法将返回一个模拟User
,除了传递的角色之外,它将为所有其他角色返回false。
要回答您的问题,您可以使用以下内容执行您想要的操作:
user = double(Hash[User.methods.grep(/\?$/).map{|m| [m, false]}])
user.stub("publisher?" => true)
以上可能有点“危险”并导致奇怪的结果,因为您正在重新定义属于User
祖先的方法,例如Kernel
和BasicObject
。要解决此问题,您可以通过执行以下操作来使用User
方法的子集:
(User.methods - RSpec::Mocks::Mock.methods)
作为最后一种选择,您可以执行以下操作:
user = double("publisher?" => true)
def user.method_missing(name, *args, &block)
false
end
但这是一个非常黑客,你可能想避免它:)