- 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.