Tips for testing: Authentication with devise_token_auth in Rails API

Previously, I wrote a blog post on how to create an authentication system for Rails API with the devise_token_auth gem.

Guide to devise_token_auth: Simple Authentication in Rails API
Risa Fujii

#rails#ruby#devisetokenauth#authentication

But to be honest, the most unintuitive part for me was not adding the authentication system itself, but testing it and any functions that use it. So this post focuses on testing, more specifically:

How do you write tests for the authentication system?
How do you write controller tests for functions that require authentication?

The first point is self-explanatory. As for the second: let’s say you’re creating an application for library books. You can add new books to the database with an HTTP POST request to /books, but you need to be authenticated to do this. If no authentication was involved, you could test the method for adding books like below, but this test would fail in our case because the request is not authenticated.
# test/controllers/books_controller_test.rb
test ‘should create new book’ do
post ‘books/’,
params: {
name: ‘Harry Potter’
}
assert_response :success
end

Note: Guide is for Linux or MacOS systems, and uses minitest for testing.

Overview

Here are the steps involved.

Write helper authorization methods that we’ll use in our tests in test/helpers/authorization_helper.rb

Create test/authorization_test.rb for testing our authorization helper code
In the tests for methods that require authentication, call the authorization methods in authorization_helper.rb

Step 1. Create authorization helper file

One solution to the problem I described above (tests failing for unauthenticated requests) is to add one step for login to the first part of each test for methods that require authentication. But this would result in a lot of redundant code. A better way to handle this is to make a helper file with the necessary authentication methods, and call those methods in your controller tests.
In test/helpers, create a file called authorization_helper.rb. Inside, write the methods you’ll use in your tests, like signing in and getting the authentication tokens from the headers (demonstrated below).
module AuthorizationHelper
def sign_up(user)
# The argument ‘user’ should be a hash that includes the params ’email’ and ‘password’.
post ‘/auth/’,
params: { email: user[:email],
password: user[:password],
password_confirmation: user[:password] },
as: :json
end

def auth_tokens_for_user(user)
# The argument ‘user’ should be a hash that includes the params ’email’ and ‘password’.
post ‘/auth/sign_in/’,
params: { email: user[:email], password: user[:password] },
as: :json
# The three categories below are the ones you need as authentication headers.
response.headers.slice(‘client’, ‘access-token’, ‘uid’)
end
end

Bear in mind that this code cannot be run as-is, since this is meant to be called inside tests.

Step 2. Test your authorization helper code

In your test folder, create a file called authorization_test.rb. Then, you can test the methods in your helper file.
For example:
test ‘sign up and log in user one’ do
user_one = { email: ‘userone@test.com’, password: ‘password’ }
sign_up(user_one)
assert_response :success

(user_one)
assert_response :success
end

Now that we’ve confirmed that authentication works, if your controller tests fail, you can assume that it’s not because of authentication issues.

Step 3. Add your helper methods to tests that require authentication

Going back to our books app example – the controller test for adding new books needs to have authentication tokens in the HTTP request. So take the following steps.

1) Require and include your helper file

Include the helper file in the controller test where you’re using authentication.
# Please use your actual relative path to this file.
require_relative ‘../helpers/authorization_helper’

Also, include this module like so.
class BooksControllerTest < ActionDispatch::IntegrationTest include AuthorizationHelper end 2) Authenticate in the setup method The setup method is called before every test, so put your authentication method here to avoid redundant code. def setup test_user = { email: 'user@test.com', password: 'testuser' } sign_up(test_user) @auth_tokens = auth_tokens_for_user(test_user) end 3) Include the auth tokens in the headers Then, simply include the auth tokens in the headers of your test. It's as simple as adding one line like below. test 'should create new book' do post 'books/', params: { name: 'Harry Potter' }, headers: @auth_tokens assert_response :success end And that's it! We've covered how to write tests for authorization code and how to send authenticated HTTP requests in other tests. Thank you for reading, and please let me know if anything needs clarifying.

Link: https://dev.to/risafj/tips-for-testing-authentication-with-devisetokenauth-in-rails-api-55pj