Join Kevin Skoglund for an in-depth discussion in this video Fixtures and factories, part of RSpec Testing Framework with Ruby.
- The testing world is a very big place. There's an entire ecosystem of tools to help solve common problems and pain points. Throughout this chapter I'm going to introduce you to some of the most common tools. After you learn the fundamentals of testing in RSpec, this is the area where you should continue your education and growth. We'll begin by talking about Fixtures and Factories. We've seen how you can create new objects and save them to a database while you're working in RSpec. There's nothing wrong with that approach, and you can get a lot of mileage out of it.
However, you may find at some point that you're having to populate several tables with data during the set up for an example. You might end up thinking, "Boy, I really wish "I could just start with 30 products "already in my products table, "and 10 customers already in my customers table, "and 15 categories already in my categories table." That's the problem that fixtures are designed to solve. Fixtures is a fancy word for sample data. Fixtures allow you to define a large set of sample data ahead of time and store them as YAML files inside your spec directory, inside another directory called fixtures.
When you're test sweep first starts up RSpec will use those fixture files to prepopulate your database tables. It's a great way to give yourself a large set of sample data that you can then use throughout all of your tests. So for example, you might have a customer fixture file that looks like this. This file is in the YAML file format. I have three different customers, Bob, Larry, and Mary Ann. The first line is just the label that I'm assigning to each one of these. We'll see how that comes into play in a moment. And then for each customer I have a list of attributes.
Each one is a key value pair with a colon in between. The indentation in the YAML format is important, each of those attributes is being indented two spaces. As you can see the YAML format is pretty straight forward and pretty easy to read. My test sweep first runs, RSpec will parse this file and add these three customers to my customers table with those attributes. Then I'll have these customers available throughout my tests. Fixtures also allow you to define relationships between records.
For example, if I have a customers fixture file that has Bob listed in it, then in an orders fixture file I can have an order that belongs to Bob, by specifying customer: bob. Now you can see how the fixture label is being used. I can refer to the fixture data in the other file by its label, Bob. We can also use erb in our fixtures. If I very quickly wanted to create five sequential orders for Bob, I can do that by using erb to create a loop. Now that we have fixture data in our tables, how can we use it in our tests? We could perform a database search in order to find the record, but that's tedious, especially since we don't know what id it was given when it was loaded into the fixtures file.
Instead we can use the label to reference the fixture that we want. We can call a specially created customers method and pass in the fixture label as a symbol. RSpec will use that label to find the database object for you. What it returns is not the fixture, but the object that was created based on the fixture. The database object. This object will have been saved to the database, so it will have an id at that point. We can also call any methods the object has, or access any relationships that it has.
An alternative to fixtures is to work with Factories. When we talk about factories we're mostly talking about the popular library named factory_girl. You can find out more information about factory_girl from their github repository. If you're going to be using it with Ruby on Rails, there's another Ruby gem that you'll need to install as well, which is factory_girl_rails. With factories your sample data is stored as Ruby code inside spec/factories. This code defines the object data, but it's not automatically loaded into the database.
Instead it serves as a convenient way for you to grab predefined sets of data whenever you need them. You can save it to the database, or you can just use a factory to grab an object that you never save to the database at all. Not reading and writing to the database has the advantage of being much faster. Let's look at an example for a customers factory. It looks similar to what we had in our fixture file. We're just using Ruby code to create sets of attributes and give them a label. Notice that I've given the first one the generic label of customer. It's a good practice to define a basic factory for each class with only the attributes that are required to create a new object.
This serves as a template for all of our other factories. You can see that I go on to define another factory for Bob, which inherits from the customer object. In this case they're exactly the same. But in the next factory Mary Ann inherits from customer too, but Mary Ann goes on to override three out of the four of the inherited values. Factories also allow you to assign object relationships. Here I have an order factory definition that references my customer factory. All I have to do is put the label on a single line as an attribute.
So as you can see I have customer in the second line for my order factory, I could also have Bob instead, if I wanted this order to be linked to Bob, and not just to the generic customer. When you want to work with factories inside RSpec mostly you're going to be using build, create, and build_stubbed. Build is going to instantiate a new object, but it will not save it to the database. Create will build the object and then save it to the database. Build_stubbed does what you would expect, it creates a new object, but it stubs out all of the attributes.
You can still build custom versions of your factories by adding attributes that will override the predefined attributes. Here you can see that I'm overriding three out of the four attributes to create a new Larry customer object. And as you would expect, we're working with real Ruby objects, so we have the ability to call object methods and to access its relationships. In my view, the Ruby community is pretty divided on the use of fixtures versus factories. Some people prefer one, while some people prefer the other. You should try them both to see which you prefer, or experiment with using them both together.
If you're going to be working with Fake Data, there are two other Ruby gems that might be useful to you. The first is called Faker. And the second is called Forgery. These gems make it easy to generate fake data in your fixtures or your factories. Whether you need 20 fake email addresses, 30 typical mailing addresses, or even 100 last names. Faker and Forgery can make that easy. When taken all together, fixtures, factories, and tools for generating fake data can help to alleviate the pain points when working with sample data in RSpec.
- 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