How to test or bypass Basic Auth in Rails 5 testing of controllers
Asked Answered
A

1

7

I would like to test controllers in Rails 5 for which Basic Authentication is activated. The way explained in the current official instruction (as of 2018-10-14) does not work for some reason. The Q&A "Testing HTTP Basic Auth in Rails 2.2+" seems too old for Rails 5 (at least for the default).

Here is a simplified example to reproduce the case.
I made an Article model and related resources by scaffold from a fresh install of Rails (latest stable version 5.2.1):

bin/rails g scaffold Article title:string content:text

and added the basic auth function to the controller, following the official guide; then the ArticlesController is like this, which certainly works:

# /app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  http_basic_authenticate_with name: "user1", password: "pass"
  before_action :set_article, only: [:show, :edit, :update, :destroy]

  def index
    @articles = Article.all
  end
end

The official instruction explains the way to test basic authentication; you add request.headers['Authorization'] in the setup block in the test file of the controller, which I did:

# /test/controllers/articles_controller_test.rb
require 'test_helper'

class ArticlesControllerTest < ActionDispatch::IntegrationTest
  setup do
    request.headers['Authorization'] =
      ActionController::HttpAuthentication::Basic.encode_credentials("user1", "pass")
    @article = articles(:one)
  end

  test "should get index" do
    get articles_url
    assert_response :success
  end
end

However, bin/rails test fails as follows:

# Running:

E

Error:
ArticlesControllerTest#test_should_get_index:
NoMethodError: undefined method `headers' for nil:NilClass
    test/controllers/articles_controller_test.rb:5:in `block in <class:ArticlesControllerTest>'

bin/rails test test/controllers/articles_controller_test.rb:10

Clearly, request method returns nil, and hence request.headers['Authorization'] fails. It is the same if the statement is placed at the top of 'testing-index' block instead.

I have found request returns a proper value after get articles_url is run, but it is too late by that time; I mean, Authentication has already failed by that time (obviously). With some googling, it seems some people use @request and @response for the purpose instead, but I have also found the they are exactly in the same situation as request (expectedly?), that is, they are nil before get.

What is the way to bypass or test Basic Auth in testing of controllers or integration tests in Rails 5?

EDIT:
"The current official instruction (as of 2018-10-14)" is apparently wrong. See the answer.

Agosto answered 14/10, 2018 at 13:38 Comment(0)
P
8

The testing docs were incorrect and have been updated but not yet released. The updated docs read:

NOTE: If you followed the steps in the Basic Authentication section, you'll need to add authorization to every request header to get all the tests passing:

post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }, headers: { Authorization: ActionController::HttpAuthentication::Basic.encode_credentials('dhh', 'secret') }
Pathy answered 14/10, 2018 at 14:3 Comment(2)
That's a perfect answer and explains! I confirm it works; n.b., for get, the params option is optional and so this works: get articles_url, headers: { Authorization: ActionController::HttpAuthentication::Basic.encode_credentials('dhh', 'secret') }Agosto
A note (Rails 5.2.1): delete method fails silently (that is, it does not delete the specified record) with no warning when Basic Auth fails (such as, it is not given!), unlike post and get, where an explicit error message is issued.Agosto

© 2022 - 2024 — McMap. All rights reserved.