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

Ruby 使用Rack::Test和Sinatra测试控制器实例变量,ruby,testing,sinatra,rack,Ruby,Testing,Sinatra,Rack,我有一个Sinatra应用程序,根据用户是否登录,将页面作为只读或可编辑页面 控制器设置一个变量@can_edit,视图使用该变量隐藏/显示编辑链接。我如何测试@我可以在测试中编辑的值吗?我不知道如何在Rack::Test下获取控制器的当前实例 我使用class\u eval在控制器中存根logged\u in?方法,但我必须检查last\u response.body以查看编辑链接是否设置了@can\u edit 如何测试@can_edit的值?不幸的是,如果不修改Rack::test,我认为

我有一个Sinatra应用程序,根据用户是否登录,将页面作为只读或可编辑页面

控制器设置一个变量
@can_edit
,视图使用该变量隐藏/显示编辑链接。我如何测试
@我可以在测试中编辑
的值吗?我不知道如何在Rack::Test下获取控制器的当前实例

我使用
class\u eval
在控制器中存根
logged\u in?
方法,但我必须检查
last\u response.body
以查看编辑链接是否设置了
@can\u edit


如何测试
@can_edit
的值?

不幸的是,如果不修改Rack::test,我认为这是不可能的。在应用程序测试期间发出请求时,Rack::Test执行以下操作:

  • 将请求添加到最近请求的列表中
  • 创建应用程序的新实例并调用其
    调用
    方法
  • 将应用程序的响应添加到最近的响应列表中
  • 很容易访问
    last_请求
    last_响应
    ,但遗憾的是,在应用程序运行时,没有保存有关应用程序状态的信息

    如果您有兴趣将一个Rack::Test补丁拼凑在一起来实现这一点,请首先查看第30行的
    Rack Test/lib/Rack/mock_session.rb
    。这是Rack::Test运行应用程序并接收标准Rack app返回值(状态、标题、正文)的地方。我的猜测是,您还必须修改应用程序,以收集并访问其所有实例变量


    无论如何,最好测试结果,而不是实现细节。如果要确保编辑链接不可见,请按DOM id测试是否存在编辑链接:

    assert last_response.body.match(/<a href="..." id="...">/)
    
    assert last_response.body.match(//)
    
    只要稍微修改一下就可以了。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进行测试

    “最好是测试结果,而不是实现细节”我以前见过类似的说法,我不同意这种说法。如果你想要2,1+1效果很好,但是1+2+10-11也一样。这两种情况都不意味着你的应用程序“实际工作正常”。然而,我并不完全同意你的观点:假设有一种方法实现不正确。如果该方法在100%的时间内返回正确答案(你无法想象它失败的情况),那么实现真的“不正确”吗?这有关系吗?如果您发现自己正在测试方法中的变量值,那么您可能没有足够的测试用例(输入)。如果重命名变量或以其他方式更改应用程序的实现而不更改其行为,则测试应在不进行修改的情况下通过。这是首先使用测试的一个重要原因。我认为测试@can_edit就是测试控制器的输出。我不喜欢在设置@can\u edit时通过检查视图来间接测试它。这应该是一个不同的测试,仅针对视图。这将为我返回nil app_实例。也许最新的西纳特拉改变了它的基类。