namespace :api,:defaults => {:format => 'json'} do scope :module => :v1,:constraints => ApiConstraints.new(:version => 1,:default => false) do resources :posts,:only => [:create,:show,:destroy,:index] end scope :module => :v2,:constraints => ApiConstraints.new(:version => 2,:default => true) do resources :posts,:index] end end
在每个路径中,我的约束是一个新的ApiConstraints对象,它位于我的./lib文件夹中.班级看起来像这样:
class ApiConstraints def initialize(options) @version = options[:version] @default = options[:default] end def matches?(req) @default || req.headers['Accept'].include?("application/vnd.MYAPP.v#{@version}") end end
现在,手动测试时,一切都按预期工作.在我的API中,每个版本可能有5到10个控制器,并且不想测试API约束对每个控制器的作用,因为这是没有意义的.我正在寻找一个测试我的API约束的规范文件,但我不确定在哪里放置该规范.
我已经尝试添加spec / routing / api_spec.rb文件来测试,但是它不能正常工作,因为它抱怨有些东西没有提供,像这样:
it "should route an unversioned request to the latest version" do expect(:get => "/api/posts",:format => "json").to route_to(:controller => "api/v1/posts") end
即使控制器正确匹配,上述错误也会引发错误.它失败并出现以下错误:
The recognized options <{"format"=>"json","action"=>"index","controller"=>"api/v1/posts"}> did not match <{"controller"=>"api/v1/posts"}>,difference: <{"format"=>"json","action"=>"index"}>.
请注意,控制器已正确确定,但由于我不想在此测试中测试格式和操作,所以错误.我想要有3个“API规范”:
>它应该将未版本化的请求路由到最新版本
>如果没有指定,它应该默认为JSON格式
>它应该在请求时返回指定的API版本
解决方法
ActionDispatch::Assertions::RoutingAssertions#assert_recognizes
route_to的参数作为expected_options哈希传递(经过一些预处理,允许它也可以理解诸如项目#index的简写样式参数).
您希望匹配route_to匹配器(即{:get =>“/ api / posts”,:format =>“json”})的哈希实际上并不是一个格式正确的参数.如果你看看the source,你可以看到我们得到通过匹配的路径
path,query = * verb_to_path_map.values.first.split(‘?’)
#first是一个确定的标志,我们期望只有一个键值对的散列.所以:format => “json”组件实际上被抛弃,并没有做任何事情.
ActionDispatch断言希望您将完整的路径动词与一套完整的控制器,动作和路径参数.所以rspec匹配器只是传递它委托给它的方法的限制.
听起来像rspec的内置route_to匹配器不会做你想要的.所以下一个建议是假定ActionDispatch会做它应该做的事情,而只是为ApiConstraints类编写规范.
为此,我首先建议不要使用默认的spec_helper. Corey Haines有一个关于how to make a faster spec helper that doesn’t spin up the whole rails app的好主意.这可能不是完美的你的例子,但我只是想我会指出,因为你只是在这里实例化基本的红宝石对象,并不真正需要任何rails魔术.您也可以尝试使用ActionDispatch :: Request&依赖关系,如果你不想像这样在这里存根请求对象.
看起来像这样
规格/ lib目录/ api_constraint.rb
require 'active_record_spec_helper' require_relative '../../lib/api_constraint' describe ApiConstraint do describe "#matches?" do let(:req) { Object.new } context "default version" do before :each do req.stub(:headers).and_return {} @opts = { :version => nil,:default => true } end it "returns true regardless of version number" do ApiConstraint.new(@opts).should match req end end end end
… aaand我会让你弄清楚如何设置上下文/写出你的其他测试的期望.