从TestCase调用Sinatra应用程序实例方法

我在Sinatra应用程序中有一个util方法,我想从我的TestCase进行测试。

问题是我不知道如何调用它,如果我只是使用app.util_method我有错误NameError: undefined local variable or method 'util_method' for #

my_app.rb:

 class MyApp < Sinatra::Base # [...] routes methods # utils methods def util_method return "hi" end end 

my_app_test.rb:

 require "my_app.rb" require "test/unit" require "rack/test" class MyAppTest < Test::Unit::TestCase include Rack::Test::Methods def app MyApp.new end # [...] routes methods tests def test_util_method assert_equal( "hi", app.util_method ) end end 

Sinatra 将new方法别名化为new! 在重新定义它之前,所以最简单的解决方案是使用它:

 def app MyApp.new! end 

当然我只注意到我提出以下内容后,我将留下,因为它可能是有用的/提供信息。


一种可能的方法来绕过Sinatra 重新定义new方法并返回一个完整的Rack应用程序来获取实际基类的实例是做“真正的” new方法自己做的事情:

 def app a = MyApp.allocate a.send :initialize a end 

这有点像黑客,但它可能对测试很有用。

另一种技术是“走”中间件堆栈直到你上课。 以下是一个有点脆弱,因为它依赖于所有涉及使用名称@app来引用堆栈中的下一个应用程序的中间件,但这是相当常见的。

 def app a = MyApp.new while a.class != MyApp a = a.instance_variable_get(:@app) end a end 

这对于尚未发布的Sinatra 1.4不起作用(至少不会在当前的master上提交41840746e866e8e8e9a0eaafc53d8b9fe6615b12 ),因为new现在返回一个Wrapper类并且循环永远不会结束。 在这种情况下,您可以直接从@instance变量中获取基类:

 def app MyApp.new.instance_variable_get :@instance end 

(注意最后一种技术可能会在最终的1.4发布之前发生变化)。

您遇到的问题是,MyApp.new不会返回MyApp的实例,而是包含App的中间件实例(通常是Rack :: Head或Sinatra :: ShowExceptions)。 可以在线程Sinatra Usage Question / Rack App中找到一个很好的解释。

我能想到的唯一解决方案是将实例方法更改为可以在没有实例本身的情况下调用的类方法。 由于您的应用程序实例可能会针对每个请求进行新实例化,因此实例方法可能与您的方案中的类方法相比没有太多优势。

编辑:

在即将到来的Sinatra 1.4中,初始化将发生变化。 Sinatra :: Base.new将返回一个Sinatra :: Wrapper实例,它暴露了#settings和#helpers。 这可能有助于解决访问Sinatra :: Base实例方法的问题。 有关更多信息,请参阅Sinatra Changelog 。