Join Kevin Skoglund for an in-depth discussion in this video Test database, part of Ruby: Testing with RSpec.
- One of the most noticeable differences about working with RSpec and a basic Ruby project versus working with Ruby on Rails is going to be the use of databases. Because if you're using Ruby on Rails, most likely you're using a database behind it, and you're gonna be reading and writing to it frequently. So your tests will need to also. In a default Rails application, you get three environments: development, production and test. If you're new to testing, then you may have only used those first two before. The idea behind all three of them is the same.
We can have unique configuration options for development, production and test, and keep them all separate. We also have three database configurations that correspond to each of those. You're probably used to using that with development and production, and you understand why you don't want your development data to be mixed in with your production data. Development changes shouldn't affect what's on production. The same is true with our test data. We wanna keep it independent from production, certainly, but also from development. If I'm developing a new feature, and I have my database in a certain state, if I stop and run my test, I don't want my test to affect what I've got in my development.
Therefore, the test database has its own database that it can work with. It can read to it, it can write to it, it can completely clear out the contents if it wants, and it's not going to affect what's happening in development or production. In order to use that test database, we're going to need to prepare it and make sure that it's ready. I mentioned earlier that if you run your specs by calling rspec spec, that it doesn't do all the same steps that running rake does. Mostly what it doesn't do is it doesn't prepare your test database for you. If you want it to prepare the test database first, you need to call rake, either rake by itself or rake spec.
And behind the scenes, what that does at the moment is it calls rake db:test:prepare. It's a rake task that prepares the database for you. Now, this actual rake task is most likely going to be deprecated and removed. It's been discussed a lot, but it hasn't been removed yet. It doesn't really matter. The point is that behind the scenes, rake spec will still prepare it for you. The actual steps that it takes to prepare it are something like this. It first drops the existing database, then it creates the new database, and then it loads in the schema file.
You can actually do these steps yourself. If you don't want rake to do it, you can type these three yourself, or rake db:prepare will do them all for you as well. At the end, you should have the same schema in your test database as you have in your development database, including any migrations that you've run, because every time we run a migration, Rails updates that schema file as well. So if we just load the schema file, we'll have a test database that has the same schema, but it will have no data in it to start. It will be empty, but with all the right tables and columns.
And then it'll be up to you to populate with data. There are a couple of points, though, about adding new data to the database. First, save objects to the database only when it's really necessary. And the reason why is that making database calls in your examples adds significant time to the examples. And a lot of times, it's really not necessary. You don't actually need to add the customer to the database to check whether the customer is valid. Sometimes an unsaved instance can do the job just as well. So you could have Product New or Product Build instead of Product Create or Product Save.
And definitely try hard not to create thirty products inside a loop and add them all to the database. That's gonna take up a lot of time. If unsaved instances won't do the trick, you might be able to use test doubles. We've already talked about how to create test doubles, and we'll cover that a bit more when we get to models. Or you might be able to stub the save action, so that you don't actually save it to the database, you just get back "true" as if you had. If it seems that I'm harping on this point a bit, it's because I think that this is the one thing that people do that slows down their test suite the most.
And I think that if you find that one of your examples is running slow, this is going to be the reason why.
- Installing and configuring RSpec
- Writing and running examples
- Defining expectations using matchers
- Using helper methods, before/after hooks, and shared examples
- Creating test doubles using mocks and stubs
- Testing Ruby on Rails with RSpec
- Putting test-driven development into practice