2014-09-12 41 views
23

Tôi đang cố đặt tiêu đề cho một số yêu cầu RSpec yêu cầu xác thực. Tiêu đề là ACCESS_TOKEN. Không có vấn đề làm thế nào tôi cố gắng để thiết lập các tiêu đề, nó không bao giờ được thiết lập. Tôi biết các ứng dụng hoạt động vì tôi có thể tự kiểm tra nó, tôi chỉ không thể nhận được các xét nghiệm rspec để làm việc. Xem mã nguồn đầy đủ & kiểm tra sự cố này tại đây: https://github.com/lightswitch05/rspec-set-header-exampleĐặt tiêu đề trong yêu cầu RSpec 3

Vì xác thực được sử dụng trong hầu hết các thông số yêu cầu của tôi, tôi đã tạo mô-đun trợ giúp hỗ trợ để truy xuất mã thông báo truy cập và đặt tiêu đề. Dưới đây là tóm tắt về cách tôi đang cố gắng để thiết lập các tiêu đề, thấy tất cả mọi thứ tôi đã cố gắng trong full source

# my_app/spec/support/session_helper.rb 
module SessionHelper 
    def retrieve_access_token 
    post api_v1_session_path({email: '[email protected]', password: 'poor_password'}) 

    expect(response.response_code).to eq 201 
    expect(response.body).to match(/"access_token":".{20}"/) 
    parsed = JSON(response.body) 
    token = parsed['access_token']['access_token'] 

    @request.headers['HTTP_ACCESS_TOKEN'] = token 
    end 
end 

một ví dụ yêu cầu spec mà sử dụng helper này và nên làm việc, nhưng luôn luôn thất bại vì tiêu đề không bao giờ được thiết lập:

# my_app/spec/requests/posts_spec.rb 
# ... 
context "create" do 
    it "creates a post" do 
    retrieve_access_token 
    post = FactoryGirl.build(:post) 

    post api_v1_posts_path(
     post: { 
     title: post.title, 
     content: post.content 
     } 
    ) 

    expect(response.body).to include('"id":') 
    expect(response.body).to include('"title":"' + post.title + '"') 
    expect(response.body).to include('"content":"' + post.content + '"') 
    expect(response.response_code).to eq 201 
    end 
end 

tôi biết tôi có thể tự thiết lập các tiêu đề trong từng getpost yêu cầu - nhưng đó không phải là một giải pháp duy trì cho phép API toàn. Hãy tưởng tượng phải thay đổi mọi thử nghiệm nếu tên tiêu đề thay đổi một chút.

Trả lời

39

Lưu ý: Câu trả lời này dựa trên những gì bạn có vẻ đang gọi api_v1_session_path với post yêu cầu SessionsController cho mọi thông số kỹ thuật bạn đang cố gắng chạy trong thông số yêu cầu của bạn.

Có hai cách để giải quyết vấn đề tôi thấy bạn có ở đây.

Giải pháp # 1 - Hoặc bạn tạo phương thức trợ giúp khác trong SessionHelper hoặc trong một số tệp trợ giúp khác được gọi là support/requests_helper.rb (tuy nhiên bạn thích). Tôi muốn tạo helper khác trong hỗ trợ/requests_helper.rb:

module RequestsHelper 
    def get_with_token(path, params={}, headers={}) 
    headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token) 
    get path, params, headers 
    end 

    def post_with_token(path, params={}, headers={}) 
    headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token) 
    post path, params, headers 
    end 

    # similarly for xhr.. 
end 

sau đó trong rails_helper.rb:

# Include the sessions helper 
    config.include SessionHelper, type: :request 
    # Include the requests helper 
    config.include RequestsHelper, type: :request 

thay đổi session_helper.rb:

# my_app/spec/support/session_helper.rb 
module SessionHelper 
    def retrieve_access_token 
    post api_v1_session_path({email: '[email protected]', password: 'poor_password'}) 

    expect(response.response_code).to eq 201 
    expect(response.body).to match(/"access_token":".{20}"/) 
    parsed = JSON(response.body) 
    parsed['access_token']['access_token'] # return token here!! 
    end 
end 

Bây giờ, bạn có thể thay đổi tất cả các thông số yêu cầu của bạn như sau:

describe Api::V1::PostsController do 

    context "index" do 
    it "retrieves the posts" do 
     get_with_token api_v1_posts_path 

     expect(response.body).to include('"posts":[]') 
     expect(response.response_code).to eq 200 
    end 

    it "requires a valid session key" do 
     get api_v1_posts_path 

     expect(response.body).to include('"error":"unauthenticated"') 
     expect(response.response_code).to eq 401 
    end 
    end 
end 

Giải pháp # 2-Thay đổi thông số kỹ thuật/nhà máy/access_token_factory.rb tới:

FactoryGirl.define do 
    factory :access_token do 
    active true 
    end 

    # can be used when you want to test against expired access tokens: 
    factory :inactive_access_token do 
    active false 
    end 
end 

Bây giờ, hãy thay đổi tất cả các yêu cầu kỹ thuật để sử dụng access_token:

describe Api::V1::PostsController do 

    context "index" do 
    let(:access_token){ FactoryGirl.create(:access_token) } 

    it "retrieves the posts" do 
     # You will have to send HEADERS while making request like this: 
     get api_v1_posts_path, nil, { 'HTTP_ACCESS_TOKEN' => access_token.access_token } 

     expect(response.body).to include('"posts":[]') 
     expect(response.response_code).to eq 200 
    end 

    it "requires a valid session key" do 
     get api_v1_posts_path 

     expect(response.body).to include('"error":"unauthenticated"') 
     expect(response.response_code).to eq 401 
    end 
    end 
end 

Tôi muốn đi với "Giải pháp # 1" vì nó loại bỏ gánh nặng khiến bạn nhớ gửi HTTP_ACCESS_TOKEN trong tiêu đề mỗi lần bạn muốn thực hiện các yêu cầu đó.

+0

Tôi đã sử dụng Giải pháp # 1 với một số thay đổi nhỏ: trong rails_helper tôi đã bao gồm chức năng trợ giúp làm loại bộ điều khiển. Và tôi phải thêm token trong helper của tôi như request.headers này ['HTTP_AUTH_TOKEN'] = Rails.application.secrets.default_api_token –

+3

Đối với bất cứ ai chỉ muốn thêm header bằng RSpec 3 và Rails 4, chỉ cần gọi 'request.headers .merge! ({'X-MYHEADER': 'value'}) 'ở bất kỳ vị trí nào trước' get', 'post', v.v. để thêm các tiêu đề vào yêu cầu cho đặc tả bộ điều khiển. Xem ý chính này để biết ví dụ https://gist.github.com/quadrolls/9203f924f934398f6992162bbbcb1a0c – Squadrons

4

Có lẽ vì bây giờ Rspec xử lý các tệp đặc tả. Nó no longer automatically infers spec type from a file location

Hãy thử một trong hai thiết lập hành vi này trở lại với những gì bạn đã từng biết

RSpec.configure do |config| 
    config.infer_spec_type_from_file_location! 
end 

hoặc thiết lập nó tại địa phương cho mỗi điều khiển file đặc tả trong dự án của bạn

describe MyController, type: :controller do 
    # your specs accessing @request 
end 
+1

trong khi thay đổi thông số yêu cầu về thông số bộ điều khiển sẽ cho phép truy cập vào 'yêu cầu' var - điều này không trả lời thông số kỹ thuật DRYest để đặt tiêu đề trong ** yêu cầu **. – lightswitch05

14

quan niệm sai lầm phổ biến là để điều trị điều khiển và yêu cầu kiểm tra như nhau.

Bạn nên bắt đầu đọc từ controller specsrequest specs. Như bạn có thể thấy, các thông số điều khiển mô phỏng yêu cầu http, trong khi các thông số yêu cầu thực hiện yêu cầu ngăn xếp đầy đủ.

Bạn có thể tìm thấy một số bài viết hay về lý do bạn nên viết thông số kỹ thuật điều khiển và những gì cần kiểm tra ở đó here. Mặc dù viết rất hay, họ không nên chạm vào cơ sở dữ liệu theo ý kiến ​​của tôi.

Vì vậy, trong khi Voxdei answer là một phần hợp lệ (sau khi thay đổi thông số yêu cầu về thông số bộ điều khiển theo cách thiết lập tiêu đề của bạn sẽ hoạt động), nó bỏ qua quan điểm của tôi.

Trong yêu cầu thông số kỹ thuật, bạn không thể chỉ sử dụng phương pháp yêu cầu/điều khiển, bạn phải vượt qua tiêu đề của bạn trong hash như là đối số thứ ba của phương pháp yêu cầu của bạn, vì vậy tức là

post '/something', {}, {'MY-HEADER' => 'value'} 

gì bạn có thể làm mặc dù là còn sơ khai xác thực như:

before do 
    allow(AccessToken).to receive("authenticate").and_return(true) 
end 

Sau đó, bạn có thể kiểm tra xác thực của mình trong một thông số để đảm bảo rằng nó hoạt động và sử dụng trước khi lọc theo thông số kỹ thuật khác. Đây cũng có lẽ là cách tiếp cận tốt hơn khi thực hiện yêu cầu bổ sung mỗi khi bạn chạy spec cần xác thực là khá lớn trên không.

Tôi cũng thấy khá thú vị pull request in grape gem cố gắng thêm hành vi tiêu đề mặc định để bạn cũng có thể thử với cách tiếp cận như vậy nếu bạn thực sự muốn sử dụng tiêu đề mặc định trong thông số yêu cầu.

2

Câu trả lời của Surya là tốt nhất. Nhưng bạn có thể KHÔ nó lên hơn một chút:

def request_with_user_session(method, path, params={}, headers={}) 
    headers.merge!('HTTP_ACCESS_TOKEN' => retrieve_access_token) 
    send(method, path, params, headers) 
end 

Ở đây bạn chỉ có một phương pháp và gọi phương thức yêu cầu bởi tham số cho method.

0

Tôi khai báo hàm xác thực yêu cầu trả về giá trị true hoặc bất kỳ giá trị nào được hàm trả về.

ApplicationController.any_instance.stub(:authenticate_request) { true } 
Các vấn đề liên quan