- Read Tutorial
- Watch Guide Video
Now that we have our FactoryGirl configuration in place, let's start to refactor our tests to use factories instead of manually creating test specs.
To start I'm going to delete the controllers subfolder in spec since we won't be using it for this application. This is mainly because integration testing is comprehensive and provides test coverage for the core features we want to build. So, we don't have to test controllers separately. Next, let's look at the post_spec.rb file in spec/features. In this file, we can simply use:
# spec/features/post_spec.rb describe 'navigate' do before do @user = FactoryGirl.create(:user) login_as(@user, :scope => :user) end # rest of the code hidden for brevity
This will create a user in place of the long code we had earlier. Here, FactoryGirl will create a new instance of user in the test database.
Let's run rspec to see if this refactoring is working, and it does!
Likewise, let's use FactoryGirl to create our two posts as well. Also, in order for our content matcher to keep working, we will have to give the exact content for each post too, like this:
# spec/features/post_spec.rb it 'has a list of posts' do post1 = FactoryGirl.create(:post) post2 = FactoryGirl.create(:second_post) visit posts_path expect(page).to have_content(/Rationale|content/) end
If you run rspec now, we have an error. It says that the method full_name is undefined. If you look at our factories in posts.rb, you'll see that our posts are not referencing users, so we'll have to add that. Next, we'll have to change our users.rb file too. We'll have to give a different email to every user, to avoid validation errors. In general, devise will not allow the application to create users with the same email address, and this is why we have to make this change.
Let's make the following changes to the factories/users.rb file:
# spec/factories/users.rb FactoryGirl.define do factory :user do first_name 'Jon' last_name 'Snow' email "test@test.com" password "asdfasdf" password_confirmation "asdfasdf" end factory :admin_user, class: "AdminUser" do first_name 'Admin' last_name 'User' email "admin@user.com" password "asdfasdf" password_confirmation "asdfasdf" end end
After making these changes, make sure to update the posts.rb file too, like this:
# spec/factories/posts.rb FactoryGirl.define do factory :post do date Date.today rationale "Some Rationale" user end factory :second_post, class: "Post" do date Date.yesterday rationale "Some more content" user end end
If you rspec again, you'll get an error saying that the email has already been taken. So, this quick fix is not working and we have to do it the hard way. What's happening here is every time when the application tries to create a user, it encounters the same email address and devise doesn't allow it to create a new user with the same email address. To fix this problem, let's use an incriminator that will increment the email address by 1, so it is unique each time.
# spec/factories/users.rb FactoryGirl.define do sequence :email do |n| "test#{n}@example.com" end factory :user do first_name 'Jon' last_name 'Snow' email { generate :email } password "asdfasdf" password_confirmation "asdfasdf" end factory :admin_user, class: "AdminUser" do first_name 'Admin' last_name 'User' email { generate :email } password "asdfasdf" password_confirmation "asdfasdf" end end
In this code, we have a block right at the top that will add 1 each time to create a unique email address. Every time, when a new email has to be generated, FactoryGirl will know to go to the sequence block to generate a new email. This way, every email will be unique.
Now, let's run rspec and everything is fine now.
Let's go back to refactoring our post_spec.rb file. I'm going to use a method called build_stubbed instead of create for creating our posts. When we use create it slows down our test process since it forces the application to touch the database, so we are using build_stubbed method when we just need to stub our data out. Essentially, this method does not hit the database, but it mimics the process, so your test performance will be much faster.
# spec/features/post_spec.rb it 'has a list of posts' do post1 = FactoryGirl.build_stubbed(:post) post2 = FactoryGirl.build_stubbed(:second_post) visit posts_path expect(page).to have_content(/Rationale|content/) end
Next, let's go to post_spec.rb file in the folder spec/models. Like in the previous files, let's use FactoryGirl to create an instance of post. However this time we'll need to use the create method since we're testing how the system works with the database itself.
# spec/models/post_spec.rb RSpec.describe Post, type: :model do describe "Creation" do before do @post = FactoryGirl.create(:post) end
Run rspec and everything should pass.
Lastly, move to user_spec.rb and make the same change here too.
# spec/models/user_spec.rb RSpec.describe User, type: :model do before do @user = FactoryGirl.create(:user) end
Everything should be working fine now. The code looks better, and we are not touching the database as frequently as before, which will help with our test performance.