我想掌握 rack-test 正在测试的应用程序实例,以便我可以模拟它的一些方法。我以为我可以简单地将应用程序实例保存在app
方法中,但由于某些奇怪的原因,它不起作用。似乎rack-test
只是使用实例来获取类,然后创建自己的实例。
我做了一个测试来证明我的问题(它需要运行 gems “sinatra”、“rack-test”和“rr”):
require "sinatra"
require "minitest/spec"
require "minitest/autorun"
require "rack/test"
require "rr"
describe "instantiated app" do
include Rack::Test::Methods
def app
cls = Class.new(Sinatra::Base) do
get "/foo" do
$instance_id = self.object_id
generate_response
end
def generate_response
[200, {"Content-Type" => "text/plain"}, "I am a response"]
end
end
# Instantiate the actual class, and not a wrapped class made by Sinatra
@app = cls.new!
return @app
end
it "should have the same object id inside response handlers" do
get "/foo"
assert_equal $instance_id, @app.object_id,
"Expected object IDs to be the same"
end
it "should trigger mocked instance methods" do
mock(@app).generate_response {
[200, {"Content-Type" => "text/plain"}, "I am MOCKED"]
}
get "/foo"
assert_equal "I am MOCKED", last_response.body
end
end
怎么rack-test
不使用我提供的实例?如何获取rack-test
正在使用的实例,以便模拟该generate_response
方法?
更新
我没有取得任何进展。事实证明,rack-test
在发出第一个请求(即get("/foo")
)时会即时创建测试实例,因此在此之前无法模拟应用程序实例。
我用 rrstub.proxy(...)
截取.new
,.new!
和.allocate
; 并添加了带有实例类名的 puts 语句和object_id
. 我还在测试类的构造函数以及请求处理程序中添加了此类语句。
这是输出:
来自构造函数:<TestSubject 47378836917780> 代理拦截新!实例:<TestSubject 47378836917780> 代理拦截新实例:<Sinatra::Wrapper 47378838065200> 来自请求处理程序:<TestSubject 47378838063980>
注意对象 ID。测试实例(从请求处理程序打印)从未通过.new
,也从未初始化。
因此,令人困惑的是,被测试的实例从未被创建,但不知何故仍然存在。我的猜测是allocate
正在使用它,但代理拦截显示它没有。我跑TestSubject.allocate
自己来验证拦截是否有效,它确实有效。
我还在测试的类中添加了inherited
、和钩子included
,并添加了打印语句,但它们从未被调用过。这让我完全不知道引擎盖下有什么样的可怕的黑魔法机架测试。extended
prepended
总结一下:测试实例是在发送第一个请求时动态创建的。被测试的实例是由邪能魔法创建的,并且躲过了所有用钩子抓住它的尝试,所以我找不到模拟它的方法。几乎感觉作者rake-test
已经竭尽全力确保在测试期间不能触及应用程序实例。
我仍在摸索解决方案。