I have a Rails 5 API only app and using knock to do JWT authenticate.
After complete the model and model spec, I start to do the request spec.
But I have no idea how to complete the authentication inside the request spec in the right way,
My users controller,
module V1
  class UsersController < ApplicationController
    before_action :authenticate_user, except: [:create]
  end
end
Application controller,
class ApplicationController < ActionController::API
  include Knock::Authenticable
  include ActionController::Serialization
end
My stupidest solution (call the get token request to get the JWT before the rest request),
context 'when the request contains an authentication header' do
  it 'should return the user info' do
    user  = create(:user)
    post '/user_token', params: {"auth": {"email": user.email, "password": user.password }}
    body = response.body
    puts body # {"jwt":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0ODgxMDgxMDYsInN1YiI6MX0.GDBHPzbivclJfwSTswXhDkV0TCFCybJFDrjBnLIfN3Q"}
    # use the retrieved JWT for future requests
  end
end
Any advice is appreciated.
With the help of Lorem's answer, I was able to implement something similar for my request spec. Sharing it here for others to see an alternate implementation.
# spec/requests/locations_spec.rb
require 'rails_helper'
RSpec.describe 'Locations API' do
  let!(:user) { create(:user) }
  let!(:locations) { create_list(:location, 10, user_id: user.id) }
  describe 'GET /locations' do
    it 'reponds with invalid request without JWT' do
      get '/locations'
      expect(response).to have_http_status(401)
      expect(response.body).to match(/Invalid Request/)
    end
    it 'responds with JSON with JWT' do
      jwt = confirm_and_login_user(user)
      get '/locations', headers: { "Authorization" => "Bearer #{jwt}" }
      expect(response).to have_http_status(200)
      expect(json.size).to eq(10)
    end
  end
end
confirm_and_login_user(user) is defined in a request_spec_helper which is included as a module in rails_helper.rb:
# spec/support/request_spec_helper.rb
module RequestSpecHelper
  def json
    JSON.parse(response.body)
  end
  def confirm_and_login_user(user)
    get '/users/confirm', params: {token: user.confirmation_token}
    post '/users/login', params: {email: user.email, password: 'password'}
    return json['auth_token']
  end
end
I'm using the jwt gem for generating my tokens as described in this SitePoint tutorial (https://www.sitepoint.com/introduction-to-using-jwt-in-rails/)
  def authenticated_header(user)
    token = Knock::AuthToken.new(payload: { sub: user.id }).token
    { 'Authorization': "Bearer #{token}" }
  end
  describe 'GET /users?me=true' do
    URL = '/v1/users?me=true'
    AUTH_URL = '/user_token'
    context 'when the request with NO authentication header' do
      it 'should return unauth for retrieve current user info before login' do
        get URL
        expect(response).to have_http_status(:unauthorized)
      end
    end
    context 'when the request contains an authentication header' do
      it 'should return the user info' do
        user  = create(:user)
        get URL, headers: authenticated_header(user)
        puts response.body
      end
    end
  end
                        Lorem's answer mostly worked for me.  I got unrecognized keyword setting headers: on the get.  I modified the authenticated_header method and put it in support/api_helper.rb so I could reuse it.  The modification is to merge the auth token into request.headers.
# spec/support/api_helper.rb
module ApiHelper
  def authenticated_header(request, user)
    token = Knock::AuthToken.new(payload: { sub: user.id }).token
    request.headers.merge!('Authorization': "Bearer #{token}")
  end
end
In each spec file testing the api, I include api_helper.rb. And I call authenticated_header just before the get statement when testing the case of valid authentication...
# spec/controllers/api/v2/search_controller_spec.rb
RSpec.describe API::V2::SearchController, type: :controller do
  include ApiHelper
...
describe '#search_by_id' do
  context 'with an unauthenticated user' do
    it 'returns unauthorized' do
      get :search_by_id, params: { "id" : "123" }
      expect(response).to be_unauthorized
    end
  end
  context 'with an authenticated user' do
    let(:user) { create(:user) }
    it 'renders json listing resource with id' do
      expected_result = { id: 123, title: 'Resource 123' }
      authenticated_header(request, user)
      get :search_by_id, params: { "id" : "123" }
      expect(response).to be_successful
      expect(JSON.parse(response.body)).to eq expected_result
    end
  end
The key lines in this second test are...
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With