Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 847
Masa Sakano Avatar asked Oct 14 '18 13:10

Masa Sakano


People also ask

How do you test basic authentication?

Testing Basic Auth with httpbinThe endpoint for Basic Auth is /basic-auth/{user}/{passwd} . For example, if you go to http://httpbin.org/basic-auth/foo/bar you'll see a prompt and you can authenticate using the username foo and the password bar .

How do I run a test case in Rails?

Or we can run a single test file by passing the bin/rails test command the filename containing the test cases. This will run all test methods from the test case. You can also run a particular test method from the test case by providing the -n or --name flag and the test's method name.

How do you run a Minitest in Rails?

To run a Minitest test, the only setup you really need is to require the autorun file at the beginning of a test file: require 'minitest/autorun' . This is good if you'd like to keep the code small. A better way to get started with Minitest is to have Bundler create a template project for you.

What type of test is not typical in a Rails application?

Integration. Don't try to test all the combinations in integration tests. That's what unit tests are for. Just test happy-paths or most common cases.


1 Answers

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') }
like image 64
shanecav Avatar answered Sep 27 '22 18:09

shanecav