Testando variáveis de instância do controlador com Rack::Test e Sinatra
Pergunta
Eu tenho um aplicativo Sinatra que exibe páginas somente leitura ou editáveis, dependendo se o usuário está logado.
O controlador define uma variável @can_edit
, que é usado pelas visualizações para ocultar/mostrar links de edição.Como posso testar @can_edit
o valor em meus testes?Não tenho ideia de como chegar à instância atual do controlador em Rack::Test.
eu uso class_eval
para esboçar o logged_in?
método no controlador, mas estou tendo que recorrer à verificação last_response.body
para meus links de edição para ver se @can_edit
foi definido ou não.
Como posso testar o valor de @can_edit
diretamente?
Solução
Infelizmente não acho que isso seja possível sem modificar Rack::Test.Quando você faz uma solicitação durante o teste do aplicativo, Rack::Test faz o seguinte:
- adiciona a solicitação a uma lista de solicitações recentes
- cria uma nova instância do seu aplicativo e invoca seu
call
método - adiciona a resposta do seu aplicativo a uma lista de respostas recentes
É fácil acessar o last_request
e last_response
, mas infelizmente nenhuma informação é salva sobre o estado do seu aplicativo enquanto ele está em execução.
Se você estiver interessado em hackear um patch Rack::Test para fazer isso, comece olhando rack-test/lib/rack/mock_session.rb
na linha 30.É aqui que Rack::Test executa sua aplicação e recebe os valores de retorno padrão do aplicativo Rack (status, cabeçalhos, corpo).Meu palpite é que você também terá que modificar seu aplicativo para coletar e tornar acessíveis todas as suas variáveis de instância.
De qualquer forma, é melhor testar os resultados e não os detalhes da implementação.Se você quiser ter certeza de que um link de edição não está visível, teste a presença do link de edição por ID do DOM:
assert last_response.body.match(/<a href="..." id="...">/)
Outras dicas
É possível com um pequeno hack. As instâncias do aplicativo Sinatra não estão disponíveis porque são criadas quando Sinatra :: Base#a chamada é chamada. Como Alex explicou. Este hack prepara uma instância à frente e deixa a próxima chamada agarrá -la.
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
Eu descobri essa técnica para zombar da instância que executa o teste atual em primeiro lugar.
Aqui está uma alternativa desagradável, mas viável
# 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
Isso permite testar um sinatra antes de filtrar e/ou acessar variáveis de instância criadas para o aplicativo base.