A simple login test with Rails, Devise and Minitest
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.
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:
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 wantencrypted_password
should be generated with Devise methodpassword_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.