One of the most common questions for Rails developers new to testing is “What are all the Rails testing tools and how do I use them?”
I’ll explain what the major tools are but I want to preface it by saying that the most important thing to learn to be a successful tester is testing principles, not testing tools. If you think of testing like a taco, the tools are the tortilla and the principles are the stuff inside the taco. The tortilla is essential but it’s really only a vehicle.
The following are the tools I use for my testing.
RSpec is a test framework. A test framework is what gives us a structure for writing our tests as well as the ability to run our tests.
There are other test frameworks but RSpec is the most popular one for commercial Rails projects. The second most popular test framework is Minitest.
Test frameworks differ syntactically but the testing principles and practices are going to be pretty much the same no matter what framework you’re using. (If you’re not sure whether you should learn RSpec or Minitest, I write about that here.)
One of the challenges of Rails testing is generating test data. For example, if you’re writing a test that logs a user in and then takes some action, you’re going to have to create a user in the database at the beginning of the test. Many tests require much more complicated test data setup.
There are two common ways of generating test data in Rails tests: fixtures and factories.
Fixtures typically take the form of one or more YAML files with some hard-coded data. The data is translated into database records one time, before any of the tests are run, and then deleted afterward. (This happens in a separate test database instance of course.)
With factories, database data is generated specifically for each test. Instead of loading all the data once at the beginning and deleting it at the end, data is inserted before each test case and then deleted before the next test case starts. (More precisely, the data isn’t deleted, but rather the test is run inside a database transaction and the data is never committed in the first place, but that’s a mechanical detail that’s not important right now.)
Relative merits of fixtures and factories
I tend to prefer factories because I like having my data generation right inside my test, close to where the test is happening. With fixtures the data setup is too distant from where the test happens.
In my experience, for whatever reason, most people who use RSpec use factories and most people who use Minitest use fixtures. If you’d like to learn more about factories and fixtures, I write more about it here.
Some Rails tests only exercise Ruby code. Other tests actually open up a browser and simulate user clicks and keystrokes.
Simulating user input this way requires us to use some sort of tool to manipulate the browser. Capybara is a library that uses Ruby to wrap a driver (usually the Selenium driver), letting us simulate clicks and keystrokes using convenient Ruby methods.
By the way, you may hear talk about Cucumber online, but I don’t use Cucumber. I think Cucumber adds an extra layer of complexity and indirection without adding any value.
For more examples of how to use Capybara, go here.
VCR and WebMock
One principle of testing is that tests should be deterministic, meaning they run the same way every time no matter what.
When an application’s behavior depends on external services (e.g. a third-party API like Stripe) it makes it harder to have deterministic tests. The tests can be made to fail by an internet connection failure or a temporary outage of the external service.
Tools like VCR and WebMock can help smooth out these challenges. VCR can let us run our tests against the real external service, but capture all the service’s responses in local files so that subsequent test runs don’t talk to the external service but rather just go off of the saved responses. That way, even if the internet connection fails or the service goes down, the tests still work.
WebMock is a tool that serves a similar purpose, although I usually use it in a more limited way. I don’t consider my test suite to be deterministic unless it doesn’t talk to the network at all, so I use WebMock to enforce that my test suite isn’t making any network requests.
Rails testing tools take some time to learn, but the important part (and perhaps more difficult part) is learning testing principles.
If you’re just getting started with Rails testing, the next step I would suggest is to learn about the different types of Rails tests and when to use them.