Test Access Controls in System Tests

Learn about system tests and access control in our Rails application.

Best practices and testing strategies

Security incidents are expensive. They derail teams from providing business value, lead to a crisis of confidence for the company, and, in many cases, expose users’ personal information to bad actors. There’s no way to absolutely prevent such incidents, but ensuring that our access controls are working is a huge help.

The clearest way to do this is to write system tests that exercise the system as different types of users. Depending on how complex our authorization needs are, we may need a lot of tests. Remember that tests are a mechanism for risk management. This means we probably don’t want to test every action against every possible role, but we do need to strategically test many roles and actions.

We highly recommend thorough testing of all authentication flows, no matter what. This is particularly important if we are using Devise because Devise outputs code we have to maintain ourselves. As for testing authorizations, this can be trickier. It requires a solid understanding of why our authorization configuration is the way it is. What problems are being solved by restricting access to various parts of the system? What is the consequence of an unauthorized person gaining access to a feature they aren’t supposed to access? If that happened, would we know it had happened?

The answers to these questions can help us know where to focus. For example, if we can’t tell who performed a critical action that is restricted to certain users, we should thoroughly test the access controls to that action.

We also want to make it as easy as possible for developers to test the authorizations around new features or to test changes to authorizations. There are two things we can do to help. The first is to make sure we have a wide variety of test users that we can create with a single line of code in a test. The second is to cultivate reusable test code to set up for an authorization-related test or verify the results of one (or both).

Strategic authorization testing

The way to cultivate both of these is to start writing our system tests and look for patterns. We should have a factory to create at least one user. As we write system tests using different types of users, extract any that we use more than once into a factory. This allows future developers ourselves included, to quickly create a user with a given role.

We will also notice patterns in how we set up our test or perform assertions, and we should extract those when we see them. The mechanism for this depends on our testing framework. For Minitest, we can follow the pattern we established with with_clues and confidence_check, by creating modules in test/support:

Get hands-on with 1200+ tech skills courses.