我目前正在开发一个Minitest扩展程序,它使用before / after_suite回调包装Minitest::Runnable.run
。 Minitest的工作方式是测试类继承自Minitest::Runnable
。此外,describe
块动态创建继承自其定义的测试类的类。所以
# Minitest::Spec inherits from Minitest::Runnable
class TestClass < Minitest::Spec
describe 'a describe block' do
it 'should say hi' do
# test here
end
end
end
生成一个继承自name
为"a describe block"
的TestClass的新类。这会在Minitest管道中向下移动并在每个类上调用Mintest::Runnable.run
(例如,self
在每种情况下最终会成为包括"a describe block"
在内的每个类。供参考:
module Minitest::Runnable
def self.run reporter, options = {}
require 'rubygems'; require 'pry'; binding.pry
filter = options[:filter] || '/./'
filter = Regexp.new $1 if filter =~ /\/(.*)\//
filtered_methods = self.runnable_methods.find_all { |m|
filter === m || filter === "#{self}##{m}"
}
with_info_handler reporter do
filtered_methods.each do |method_name|
run_one_method self, method_name, reporter
end
end
end
end
我的扩展程序旨在让您拥有一个before_suite
方法来包装Minitest :: Runnable.run:
class TestClass < Minitest::Spec
before_suite do
# do something
end
describe 'a describe block' do
it 'should say hi' do
# test here
end
end
end
我已经完成了它,但是我遇到了继承问题。当我重新定义包装器方法时,我的新单例方法的接收器即使对于子类也是TestClass
(例如甚至对于descibe块)。这导致我的测试无法找到。
以下是我目前在套件前的实现:
module Minitest::SuiteCallbacks
def self.extended(base)
base.class_eval do
class << self
def before_suite(&before_suite_proc)
@before_suite_proc = before_suite_proc
context = self
original_singleton_run = method(:run)
define_singleton_method :run do |*args, &block|
# `self` here winds up being `TestClass` instead of the dynamic class
# (e.g. self.name => "TestClass" instead of "a describe block")
context.setup_before_suite
original_singleton_run.call(*args, &block)
end
end
end
end
end
end
这会抓取run
的当前实现并将其包装起来。问题在于,当从动态"a describe block"
子类的上下文调用它时,方法内的self
是TestClass
而不是动态类。
有什么想法我可以做些什么来解决这个问题?我的目标是能够动态地将任何方法包装在继承链中,并使其适用于所有子类。
提前致谢!
答案 0 :(得分:1)
错误在于方法绑定。我要么必须unbind
方法,要么打开单例类并通过UnboundMethod
引用instance_method
:
def before_suite
# ...
original = instance_method(:run)
class << self
define_method :run do |*args, &block|
# add decorated logic
original.bind(self).call(*args, &block)
end
end
end
这是我最初的猜测,但错误来自堆栈的进一步下降(我使用after_suite再次包装run方法并忘记更改该实现)。如有疑问,请将其评论出来!