Contuining my love affair with Minitest and fixtures, I wanted to dive in to something deeper this time. Switching tools takes time to get used to and managing passwords was one of the bigger headaches I encountered.

The Problem

I wanted to use Capybara for integration testing. The application uses the standard email/password combo to authenticate users via has_secure_password .

Because plain text passwords are not stored in the database, but rather a password_digest , I didn’t have a good way to fill in the login forms using Capybara to test against pages that require authentication.

The Solution

I started with a simple users.yml fixture that looked like this:

fred: first_name: Fred last_name: Flintstone email: fred@flintstone.com title: CEO company: flintstone admin: true

Bcrypt

As mentioned above, has_secure_password uses Bcrypt to encrypt the plain text passwords provided. The specific implementation to create the password digest is:

BCrypt::Password.create("password", cost: 4)

This method takes the plain text password (“password”) and encrypts it with what it calls the “cost”. It’s not terribly important for this article, but to save some speed, I’ve specified a cost of 4 — the minimum cost support by the Bcrypt algorithm. The default is 10, which will make your production applications safer. However, for testing, we don’t care.

The result of the method above looks like this:

irb(main):001:0> require "bcrypt" => true irb(main):002:0> BCrypt::Password.create("password", cost: 4) => "$2a$04$gw09FM67MDnzduXmlK46BOsdVTtzWKaSIkAdmnF/sJSLgcQhJBAUe"

That output value is what’s being stored in our database when a user inputs and saves a password. From the application’s standpoint, we could print a value like this in to our fixtures, but what’s the fun in that?!?!

ERB in Fixtures

Since fixtures allow us to use ERB in them, we could provide the Bcrypt method above to produce the password digest like so:

fred: first_name: Fred last_name: Flintstone email: fred@flintstone.com password_digest: <%= BCrypt::Password.create("password", cost: 4) %> title: CEO company: flintstone admin: true

The downside is that the actual password (“password”) is here in plain text. To fill in our login form using Capybara, we don’t have a variable to access to the get the password — we literally have to type “password”. So this isn’t the most DRY thing in the world. While it would certainly work, I think we can do better…

Capybara

Let’s say I have a test that looks like this:

visit signin_path fill_in "email", with: user.email fill_in "password", with: "password" click_on "Sign in"

Extract a Module

Our goal was to not sprinkle these plan text passwords all over the place. So let’s extract a module call TestPasswordHelper and put the plain text password in there:

require "bcrypt" module TestPasswordHelper def default_password_digest BCrypt::Password.create(default_password, cost: 4) end def default_password "password" end end

We’ll have our default password accessible via a method named…get this, default_password ! The module also contains a method ( default_password_digest ) that will allow us to send the password digest to the fixture using the Bcrypt algorithm we explored above.

Now that we have a helper module ready all set up, we add the following to our test/test_helper.rb to make these methods accessible in our tests:

require "support/test_password_helper" class ActiveSupport::TestCase include TestPasswordHelper end

With these methods mixed in, we can update our Capybara test to use the default_password method:

visit signin_path fill_in "email", with: user.email fill_in "password", with: default_password click_on "Sign in"

Helpers in Fixtures

Unfortunately, those helpers aren’t available in the fixtures.

ActiveRecord::FixtureSet is the class that gives our fixtures life. We can use Ruby to include functionality from our test helper, that will give us access to to the default_password_digest method, which reads our default_password so we don’t have to type it out.

The Rails API guide for fixtures states that helper methods should be added to ActiveRecord::FixtureSet.context_class .

So back in our test_helper.rb , we can mix in our test helpers methods like so:

ActiveRecord::FixtureSet.context_class.send :include, TestPasswordHelper

Now, back in our users.yml fixture, we can use the new default_password_digest method:

fred: first_name: Fred last_name: Flintstone email: fred@flintstone.com password_digest: <%= default_password_digest %> title: CEO company: flintstone admin: true

We can now run our tests and verify the fixtures properly insert the digest using the default password and the Capybara tests reference that same default password.

Now, if in the future we wanted use a different password for some reason, we’d only have one place to change it, and the rest of the system would follow along.

Summary

One of the things I’m constantly reminding myself during this process is whenever I have a problem, to step back and think about ways the Ruby language can help solve it rather than looking for some special sauce or gem to get me through the turmoil. Minitest is just Ruby — as most other gems are. Minitest generally provides enough utility to get us through the bigger use cases, but when it comes to special cases, it’s not there to hold our hand. That’s when we put our big boy pants on and make use of the language we’ve all come to know and love — Ruby!