Подтвердить что ты не робот

Вызов метода экземпляра приложения Sinatra из TestCase

У меня есть метод утилиты в приложении Sinatra, и я бы хотел протестировать его из 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
4b9b3361

Ответ 1

Sinatra псевдонизирует метод new до new!, прежде чем переопределять его, поэтому самым простым решением является использование этого:

def app
  MyApp.new!
end

Конечно, я только заметил, что после того, как Id придумает следующее, на которое я ухожу, поскольку он может быть полезным/информативным.


Возможный способ обойти 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, хотя (по крайней мере, не для текущего мастера, который совершает 41840746e866e8e8e9a0eaafc53d8b9fe6615b12), поскольку new теперь возвращается a Wrapper класс, и цикл никогда не заканчивается. В этом случае вы можете захватить базовый класс непосредственно из переменной @instance:

def app
  MyApp.new.instance_variable_get :@instance
end

(обратите внимание, что этот последний метод может измениться до окончательной версии 1.4).

Ответ 2

Проблема, с которой вы сталкиваетесь, заключается в том, что MyApp.new не возвращает экземпляр MyApp, а экземпляр промежуточного ПО, обертывающий ваше приложение (обычно Rack:: Head или Sinatra:: ShowExceptions). Хорошее объяснение можно найти в потоке Sinatra Usage Question/Rack App.

Единственное решение, о котором я могу думать, - это изменить метод экземпляра на метод класса, который можно вызвать без самого экземпляра. Поскольку экземпляр вашего приложения может быть только что создан для каждого запроса, метод экземпляра, вероятно, не имеет больших преимуществ перед методом класса в вашем сценарии.

Edit:

В предстоящей Sinatra 1.4 будет изменена инициализация. Sinatra:: Base.new вернет экземпляр Sinatra:: Wrapper, который предоставляет #settings и #helpers. Это может помочь решить проблему доступа к методам Sinatra:: Base. Для получения дополнительной информации см. Sinatra Changelog.