Ruby:在代码块中更改类静态方法

Phư*_*yễn 2 static-methods ruby-on-rails mocking

给出Thread类的当前方法.现在在测试中,我想这样做:

def test_alter_current_thread
  Thread.current = a_stubbed_method
  # do something that involve the work of Thread.current
  Thread.current = default_thread_current
end
Run Code Online (Sandbox Code Playgroud)

基本上,我想在测试方法中改变类的方法并在之后恢复它.我知道它对于另一种语言来说听起来很复杂,比如Java和C#(在Java中,只有强大的模拟框架可以做到).但它是红宝石,我希望这些令人讨厌的东西可用

mik*_*kej 6

您可能想看看像Mocha这样的Ruby模拟框架,但是在使用纯Ruby方面,可以使用alias_method(此处的文档)来完成,例如

预先:

class Thread
  class << self
    alias_method :old_current, :current
  end
end
Run Code Online (Sandbox Code Playgroud)

然后定义你的新方法

class Thread
  def self.current
    # implementation here
  end
end
Run Code Online (Sandbox Code Playgroud)

然后恢复旧方法:

class Thread
  class << self
    alias_method :current, :old_current
  end
end
Run Code Online (Sandbox Code Playgroud)

更新以说明在测试中执行此操作

如果要在测试中执行此操作,可以按如下方式定义一些辅助方法:

def replace_class_method(cls, meth, new_impl)
  cls.class_eval("class << self; alias_method :old_#{meth}, :#{meth}; end")
  cls.class_eval(new_impl)
end

def restore_class_method(cls, meth)
  cls.class_eval("class << self; alias_method :#{meth}, :old_#{meth}; end")
end
Run Code Online (Sandbox Code Playgroud)

replace_class_method期望一个类常量,类方法的名称和新方法定义作为字符串.restore_class_method获取类和方法名称,然后将原始方法替换为原位.

您的测试将遵循以下方式:

def test
  new_impl = <<EOM
  def self.current
    "replaced!"
  end
EOM
  replace_class_method(Thread, 'current', s)
  puts "Replaced method call: #{Thread.current}"
  restore_class_method(Thread, 'current')
  puts "Restored method call: #{Thread.current}"
end
Run Code Online (Sandbox Code Playgroud)

您还可以编写一个小的包装器方法,它将替换方法,屈服于块,然后确保之后恢复原始方法,例如

def with_replaced_method(cls, meth, new_impl)
    replace_class_method(cls, meth, new_impl)
    begin
        result = yield
    ensure
        restore_class_method(cls, meth)
    end
    return result
end
Run Code Online (Sandbox Code Playgroud)

在您的测试方法中,这可以用作:

with_replaced_method(Thread, 'current', new_impl) do
  # test code using the replaced method goes here
end
# after this point the original method definition is restored
Run Code Online (Sandbox Code Playgroud)

正如原始答案中所提到的,你可能会找到一个框架来为你做这个,但希望上面的代码无论如何都是有趣和有用的.