ruby-on-rails – 如何使用rspec测试路由约束

我正在开发一个主要用作API的应用程序(除了一些小的视图,如会话/注册,这将是“标准”).我喜欢在 Railscast #350: Versioning an API完成的方法,所以跟着它.我的路线看起来像:
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版本

有没有人有写这些路线的规格的经验?我不想为API中的每个控制器添加规范,因为它们不对此功能负责.

解决方法

Rspec的route_to匹配者代表到 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我会让你弄清楚如何设置上下文/写出你的其他测试的期望.

相关文章

validates:conclusion,:presence=>true,:inclusion=>{...
一、redis集群搭建redis3.0以前,提供了Sentinel工具来监控各...
分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣...
上一篇博文 ruby传参之引用类型 里边定义了一个方法名 mo...
一编程与编程语言 什么是编程语言? 能够被计算机所识别的表...
Ruby类和对象Ruby是一种完美的面向对象编程语言。面向对象编...