Ruby 使用Rack::Test和Sinatra测试控制器实例变量
我有一个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,我认为
@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_实例。也许最新的西纳特拉改变了它的基类。