Тестирование переменных экземпляра контроллера с помощью Rack::Test и Sinatra
Вопрос
У меня есть приложение Sinatra, которое предоставляет страницы только для чтения или для редактирования в зависимости от того, вошел ли пользователь в систему.
Контроллер устанавливает переменную @can_edit
, который используется представлениями для скрытия/отображения ссылок редактирования.Как я могу проверить @can_edit
значение в моих тестах?Я понятия не имею, как получить доступ к текущему экземпляру контроллера в Rack::Test.
я использую class_eval
заглушить logged_in?
метод в контроллере, но мне приходится прибегать к проверке last_response.body
для моих ссылок на редактирование, чтобы узнать, @can_edit
установлен или нет.
Как я могу проверить значение @can_edit
напрямую?
Решение
К сожалению, я не думаю, что это возможно без изменения Rack::Test.Когда вы делаете запрос во время тестирования приложения, Rack::Test выполняет следующее:
- добавляет запрос в список последних запросов
- создает новый экземпляр вашего приложения и вызывает его
call
метод - добавляет ответ вашего приложения в список последних ответов
Легко получить доступ к last_request
и last_response
, но, к сожалению, никакая информация о состоянии вашего приложения во время его работы не сохраняется.
Если вы хотите собрать для этого патч Rack::Test, начните с просмотра rack-test/lib/rack/mock_session.rb
в строке 30.Здесь Rack::Test запускает ваше приложение и получает стандартные возвращаемые значения приложения Rack (статус, заголовки, тело).Я предполагаю, что вам также придется изменить свое приложение, чтобы собрать и сделать доступными все его переменные экземпляра.
В любом случае лучше тестировать результаты, а не детали реализации.Если вы хотите убедиться, что ссылка редактирования не видна, проверьте наличие ссылки редактирования по идентификатору DOM:
assert last_response.body.match(/<a href="..." id="...">/)
Другие советы
Это возможно с небольшим хаком.Экземпляры приложения Sinatra недоступны, поскольку они создаются при вызове Sinatra::Base#call.как объяснил Алекс.Этот хак подготавливает экземпляр вперед и позволяет следующему вызову его захватить.
require 'something/to/be/required'
class Sinatra::Base
@@prepared = nil
def self.onion_core
onion = prototype
loop do
onion = onion.instance_variable_get('@app')
return onion if onion.class == self || onion.nil?
end
end
def self.prepare_instance
@@prepared = onion_core
end
# Override
def call(env)
d = @@prepared || dup
@@prepared = nil
d.call!(env)
end
end
describe 'An Sinatra app' do
include Rack::Test::Methods
def app
Sinatra::Application
end
it 'prepares an app instance on ahead' do
app_instance = app.prepare_instance
get '/foo'
app_instance.instance_variable_get('@can_edit').should be_true
end
end
Я придумал эту технику, чтобы издеваться над экземпляром, который запускает текущий тест в первую очередь.
Вот неприятная, но жизнеспособная альтернатива
# app.rb - sets an instance variable for all routes
before do
@foo = 'bar'
end
# spec.rb
it 'sets an instance variable via before filter' do
my_app = MySinatraApplication
expected_value = nil
# define a fake route
my_app.get '/before-filter-test' do
# as previously stated, Sinatra app instance isn't avaiable until #call is performed
expected_value = @foo
end
my_app.new.call({
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/before-filter-test',
'rack.input' => StringIO.new
})
expect(expected_value).to eq('bar')
end
Это позволяет вам протестировать Sinatra перед фильтрацией и/или получить доступ к переменным экземпляра, созданным для базового приложения.