问题
如何实现以下stub方法:
user = User.new
user.login? #=> false
user.stub(:login?){ 'totally tubbed' }
user.login? #=> 'totally stubbed'
User.new.login? #=> false
分析
不能直接patch在当前对象的类上,需要patch在当前对象的singleton_class上:
module Stubber
extend self
def mystub(method, to:nil, &blk)
singleton = class << to||self; self end
singleton.send(:define_method, method, &blk) # define_method is private for singleton_class: use 'send' to bypass
end
end
注意:
- 可以用
class<<obj; self end
取到singleton_class - singleton_class的
define_method
是私有的,得用.send(:define_method,*args)
测试代码:
require_relative '../stubber'
require 'ostruct'
shared_examples_for 'stubbed' do
it "should stub obj" do
expect(after_stub.name).to eq 'stub'
expect(other_obj.name).not_to eq 'stub'
end
end
describe Stubber do
let(:obj) {OpenStruct.new(name:'obj')}
let(:other_obj) {OpenStruct.new(name:'obj')}
let(:after_stub) do
Stubber.mystub(:name, to:obj){ 'stub'}
obj
end
it_behaves_like 'stubbed'
context "when include Stubber in a class" do
let(:foo_class) do
Class.new do
include Stubber
def name; 'foo' end
end
end
let(:obj) {foo_class.new}
let(:other_obj) {foo_class.new}
let(:after_stub) do
obj.mystub(:name){'stub'}
obj
end
it_behaves_like 'stubbed'
end
end
网友评论