A simple login test with Rails, Devise and Minitest

Yuta Fujii
4 min readJun 5, 2019

Motivation

For Rails developers, it seems to be often that you use Devise gem for user authentication.

In such an app, most of your app’s pages might require login to access to.

Let’s say you are a TDD enthusiast, and you’re going to test controller methods (or views, integration tests). If you want to test something that would happen after login, then you need to generate an automated login process in your test code to check that. But how?

The troublesome is that an automated login is a little tricky especially for people new to write test codes.

So, let me give you a sample boilerplate for this automated login with Devise gem. If you’re with RSpec and FactoryBot, have a look at this article!

Source files are on my Github, feel free to have a look!

Demonstration of the issue

In this sample case, I generated User model through Devise and Story model. Also, I created routes and defined controller method ( stories#index) in order to demonstrate the problem.

Let’s test “some_actions_to_be_tested” in the StoriesController.

app/controllers/stories_controller.rb

As I just write a temporary method name to be developed after writing appropriate test code, the test should see an error and tell us something like “undefined local variable or method”.

In a rails app we have test directory by default, it looks like something:

/test
├── application_system_test_case.rb
├── controllers
│ └── stories_controller_test.rb
├── fixtures
│ ├── files
│ ├── stories.yml
│ └── users.yml
├── helpers
├── integration
├── mailers
├── models
│ ├── story_test.rb
│ └── user_test.rb
├── system
│ └── stories_test.rb
└── test_helper.rb

The simplest test case would be like to test if it returns 200 when the controller receives a request. So a test code would be:

test/controllers/stories_controller_test.rb

If you run the test, it fails. But the failure is not what we expected. It says “we got unexpected 302”, which is strange.

$ rails test test/controllers/stories_controller_test.rb# Running:FFailure:
StoriesControllerTest#test_should_get_index [/Users/yutafujii/code/yutafujii0/medium/tdd_rails/test/controllers/stories_controller_test.rb:6]:
Expected response to be a <2XX: success>, but was a <302: Found> redirect to <http://www.example.com/users/sign_in>
Response body: <html><body>You are being <a href="http://www.example.com/users/sign_in">redirected</a>.</body></html>

What happens here is that when you requested /stories Devise redirected you to the login page. Give up testing? No!!

Solution

We need to modify 3 files in the test folder:

  • test/test_helper.rb
  • test/controllers/stories_controller_test.rb
  • test/fixtures/users.yml

Here are the modified files and I added some notes or way of thinking for each one.

test/test_helper.rb

[Notes]

  • Include helper methods that have login feature
  • Warden is what Devise is based on. It is a general Rack authentication framework created by Daniel Neighman
  • Define log_in method, calling either Devise or Warden method

test/controllers/stories_controller_test.rb

[Notes]

  • Add set_up that includes login process
  • ⚠️ Be careful ⚠️ when you google this issue, you’ll see some article tell you to change class inheritance (parent class to ActionController::TestCase) but this won’t solve the problem

test/fixtures/users.yml

[Notes]

  • Add user fixture
  • email would be whatever you want
  • encrypted_password should be generated with Devise method password_digest (send is ruby built-in method) and described as erb format
  • This column is defined by Devise gem by the way
  • password_digest method encrypts given password string
  • password would be whatever you want

And here if you run the test, you’ll see that it actually get into index method of the controller!!

$ rails test test/controllers/stories_controller_test.rb# Running:EError:
StoriesControllerTest#test_should_get_index:
NameError: undefined local variable or method `some_actions_to_be_tested' for #<StoriesController:0x00007fbb905a4c60>
app/controllers/stories_controller.rb:5:in `index'
test/controllers/stories_controller_test.rb:16:in `block in <class:StoriesControllerTest>'

Now you can feel free to develop whatever feature you want and test it!

Happy TDDing 🎉

Resources

If you want to know more around this topic, these are the documents and articles that I think is worth reading.

--

--

Yuta Fujii

Web developer, Data analyst, Product Manager. Ex investment banker( structured finance ). Learn or Die.