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



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

问题是我不知道如何调用它,如果我只使用app.util_method,我会出现错误NameError: undefined local variable or method 'util_method' for #<Sinatra::ExtendedRack:0x007fc0c43305b8>

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上不起作用(至少在当前的主控器上不起,即commit 418040746e866e8e8e9a0eaafc53d8b9fe6615b12),因为new现在返回一个Wrapper类,循环永远不会结束。在这种情况下,您可以直接从@instance变量获取基类:

def app
  MyApp.new.instance_variable_get :@instance
end

(请注意,最后一项技术可能会在最终的1.4版本之前发生变化)。

您遇到的问题是,MyApp.new不会返回MyApp的实例,而是返回包裹应用程序的中间件的实例(通常为Rack::Head或Sinatra::ShowExceptions)。一个很好的解释可以在线程Sinatra使用问题/机架应用程序中找到。

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

编辑:

在即将推出的Sinatra 1.4中,初始化将发生变化。Sinatra::Base.new将返回一个Sinatra::Wrapper实例,该实例公开#settings和#helpers。这可能有助于解决访问Sinatra::Base实例方法的问题。有关更多信息,请参阅Sinatra变更日志。

相关内容

最新更新