简体   繁体   English

使用Rack :: Test和Sinatra测试控制器实例变量

[英]Testing controller instance variables with Rack::Test and Sinatra

I have a Sinatra app that serves pages as read-only or editable depending on if the user is logged in. 我有一个Sinatra应用程序,它以只读或可编辑的方式提供页面,具体取决于用户是否已登录。

The controller sets a variable @can_edit , that is used by the views to hide/show edit links. 控制器设置变量@can_edit ,视图使用该变量来隐藏/显示编辑链接。 How can I test @can_edit 's value in my tests? 如何在测试中测试@can_edit的值? I have no idea how to get at the current instance of the controller under Rack::Test. 我不知道如何在Rack :: Test下获取控制器的当前实例。

I use class_eval to stub the logged_in? 我使用class_eval来存根logged_in? method in the controller, but I'm having to resort to checking last_response.body for my edit links to see if @can_edit has been set or not. 控制器中的方法,但是我不得不求助于检查last_response.body以获取我的编辑链接,看看是否已经设置了@can_edit

How can I test the value of @can_edit directly? 如何直接测试@can_edit的值?

Unfortunately I don't think this is possible without modifying Rack::Test. 不幸的是,如果不修改Rack :: Test,我认为这是不可能的。 When you make a request during application testing, Rack::Test does the following: 当您在应用程序测试期间提出请求时,Rack :: Test会执行以下操作:

  1. adds the request to a list of recent requests 将请求添加到最近请求列表中
  2. creates a new instance of your application and invokes its call method 创建应用程序的新实例并调用其call方法
  3. adds your application's response to a list of recent responses 将您的应用程序的响应添加到最近的响应列表中

It's easy to access the last_request and last_response , but unfortunately no information is saved about the state of your application while it's running. 访问last_requestlast_response很容易,但遗憾的是,在应用程序运行时没有保存有关应用程序状态的信息。

If you're interested in hacking together a Rack::Test patch to do this, start by looking at rack-test/lib/rack/mock_session.rb on line 30. This is where Rack::Test runs your application and receives the standard Rack app return values (status, headers, body). 如果你有兴趣将Rack :: Test补丁一起rack-test/lib/rack/mock_session.rb这里, rack-test/lib/rack/mock_session.rb查看第30行的rack-test/lib/rack/mock_session.rb 。这是Rack :: Test运行你的应用程序并接收标准机架应用程序返回值(状态,标题,正文)。 My guess is that you're going to have to modify your application as well, to collect and make accessible all of its instance variables. 我的猜测是,您还必须修改您的应用程序,以收集并使其所有实例变量可访问。

In any case, it's best to test for results, not implementation details. 无论如何,最好测试结果,而不是实现细节。 If you want to make sure an edit link is not visible, test for the presence of the edit link by DOM id: 如果要确保编辑链接不可见,请按DOM ID测试是否存在编辑链接:

assert last_response.body.match(/<a href="..." id="...">/)

It is possible with a little hack. 有点黑客可能。 Sinatra app's instances are unavailable because they are created when Sinatra::Base#call is called. Sinatra应用程序的实例不可用,因为它们是在调用Sinatra :: Base#调用时创建的。 as Alex explained. 正如Alex解释的那样 This hack prepares an instance on ahead and let the next call grab it. 这个hack准备一个实例,让下一个调用抓住它。

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

I figured out this technique to mock the instance that runs the current test in the first place. 我想出了这种技术来模拟首先运行当前测试的实例

Heres a nasty but viable alternative 这是一个讨厌但可行的选择

# 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

This allows you to test against a sinatra before filter and or access instance variables created for base application. 这允许您在过滤之前测试sinatra,或者访问为基本应用程序创建的实例变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM