2013-04-17 5 views
16

저는 주로 API로 제공되는 응용 프로그램 ("표준"이 될 세션/등록과 같은 몇 가지 사소한보기 제외)에서 작업하고 있습니다. 나는 Railscast #350: Versioning an API에서 완성 된 접근법을 좋아하고 그렇게 따라 갔다. 각 경로에서rspec을 사용하여 경로 제약 조건을 테스트하는 방법

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, :only => [:create, :show, :destroy, :index] 
    end 
end 

, 내 제약은 새로운 ApiConstraints 내 ./lib 폴더에 위치한, 객체입니다 내 경로는 다음과 같다. 이 클래스는 다음과 같습니다.

이제 테스트 할 때 모든 것이 예상대로 작동합니다. 내 API에서는 버전 당 5 ~ 10 개 정도의 컨트롤러가있을 수 있으며 각 컨트롤러마다 API 제약 조건이 작동하는지 테스트하고 싶지는 않습니다. 내 API 제약 조건을 테스트하는 하나의 spec 파일을 찾고 있는데 그 스펙을 어디에 넣을 지 확신 할 수 없습니다.

나는 일을 테스트하는 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 내부의 모든 컨트롤러에 대한 사양을 추가하고 싶지 않습니다. ActionDispatch::Assertions::RoutingAssertions#assert_recognizes

답변

4

RSpec에의 route_to 정규 위임 route_to에 인수가 (그것도 items#index 같은 속기 스타일의 인수를 이해 할 수 있도록 몇 가지 사전 처리 후) expected_options 해시로 전달됩니다.

route_to 정규 표현식 (즉, {:get => "/api/posts", :format => "json"})과 일치 할 것으로 예상되는 해시는 실제로는 expect의 올바른 형식의 인수가 아닙니다. 당신이 the source 보면, 당신은 우리가 경로가 #first 우리가 하나의 키 - 값 쌍으로 해시를 기대하고 확실한 표시이다

path, query = *verb_to_path_map.values.first.split('?')

를 통해에 일치하고 있다는 사실을 알 수있었습니다. 따라서 :format => "json" 구성 요소는 실제로 폐기되고 아무 것도 수행하지 않습니다.

ActionDispatch 어설 션은 전체 경로 + 동사를 컨트롤러, 동작, & 경로 매개 변수 집합과 일치시키는 것으로 기대합니다.따라서 rspec matcher는 위임하는 메소드의 한계를 그냥 지나치고 있습니다.

rspec의 기본 제공 route_to 정규 표현식이 원하는대로 작동하지 않는 것 같습니다. 따라서 다음 제안은 ActionDispatch이 수행해야 할 작업을 수행하고 대신 ApiConstraints 클래스의 사양을 작성하는 것으로 가정합니다.

이렇게하려면 먼저 을 사용하여 이 아니고이 아닌 것이 좋습니다. Corey Haines의 멋진 요지는 how to make a faster spec helper that doesn't spin up the whole rails app입니다. 사실 그대로의 경우에는 완벽하지 않을 수도 있지만 여기서는 기본적인 루비 객체를 인스턴스화하고 실제로 모든 레일 마법이 필요하지 않으므로 지적하고 싶습니다. 또한 여기에서와 같이 요청 객체를 스텁 처리하지 않으려면 ActionDispatch::Request & 종속성을 요구할 수도 있습니다. 보일 것이다

뭔가

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 

spec/lib/api_constraint.rb

같은 ... aaand 난 당신이/컨텍스트를 설정하여 다른 시험에 대한 기대를 작성하는 방법을 정확하게 파악 드리겠습니다.

+0

네, 맞습니다. 이상적으로, 나는 api 스펙 파일에서 3 가지 테스트를 원한다. 하나는 기본 포맷이 작동하는지 테스트한다. 하나는 버전이 지정되지 않았을 때 유효한 컨트롤러로 라우트하는 것을 테스트하고, 다른 하나는 올바른 버전으로 라우트하는 것을 테스트한다. 버전 IS가 지정되었습니다. –

+1

글쎄,'route_to'를 사용하면'expect (: get => "/api/posts.json"').to route_to (: controller =>"api/v1/posts "와 같은 더 구체적인 기대치를 제공해야합니다. action => "index", : format => "json")'. 불행하게도 기본 rspec-rails matchers를 사용하는 방법은 없습니다. – gregates

+0

그 문제는 모든 사양이 다른 모든 사양에서 로직을 테스트한다는 것입니다. 그것은 본질적으로 모든 사양을 하나의 테스트로 압연하고 있으며, 이는 이상적이지 않습니다. –

관련 문제