Home > Blockchain >  Factory bot and problems with loging in in RSpec
Factory bot and problems with loging in in RSpec

Time:02-02

I've got a problem with Factory bot and logging in as a designated user. I'm trying to run a simple Edit test in rspec. Here it is:

require "rails_helper"

RSpec.describe "Treat management", :type => :system do
  before do
    treat =    FactoryBot.create(:treat)
    user = build(:user, email: '[email protected]', password: 'password')
    login_as(user)
    driven_by(:selenium_chrome_headless)
  end
  it "enables me to edit treats" do
    visit root_path
    click_button 'Edit'
    fill_in 'Name', with: 'A new name'
    fill_in 'Content', with: 'A new content'
    click_button "Update Treat"

    expect(page).to have_text("Treat was edited successfully")
  end
end

And here is my Treat factory. Treats have a name, content and a giver and a receiver foreign keys

FactoryBot.define do
  factory :treat do
     name {'my first factory treat'}
     content {'this is my first treat created by a factory'}
      giver factory: :user
     receiver factory: :user
  end
end

And of course the user factory. Users are defined by email and password

FactoryBot.define do
  factory :user do
     email {Faker::Internet.email}
     password {'password'}
  end
end

And you have to know the edit buttom is only present when the logged user is also the giver. I have asked around and supposedly my Treat factory is is well configured. Please help me solve this. If any other parts of code are required please let me know in comments and I'll update accordingly. And of course I know that there is a simplier way to write this test but the use of the factories is a requirement.

1

I have tried hardcoding the user in the factory (without the Faker gem) but that trigers the validation error - the email has been taken.

CodePudding user response:

Right now FactoryBot.create(:treat) will create a User for giver and User for receiver based on the Factory definition.

FactoryBot.define do
  factory :treat do
     name {'my first factory treat'}
     content {'this is my first treat created by a factory'}
     giver factory: :user # tells the factory to create a User from the User Factory
     receiver factory: :user # tells the factory to create a User from the User Factory
  end
end

You are calling this in your test but then creating a third user to test with

 before do
    treat = FactoryBot.create(:treat) # 2 users created 
    # changed to `create` since as @max pointed out `build` does not actually create a `User` 
    user = create(:user, email: '[email protected]', password: 'password') # third user
  end

This third user is neither the giver or receiver of the Treat which is why your test fails.

Instead you can override definitions in the Factory by passing arguments to create. In this case you want the User object under test to be the giver of the Treat so we can achieve this as follows (I used modified version of @max's test scheme as it is the preferred way to set this up)

require "rails_helper"
RSpec.describe "Treat management", type: :system do

  let(:user) { create(:user) }
 
  before do 
    driven_by(:selenium_chrome_headless)
  end

  context 'A Treat#giver' do 
    let!(:treat) {create(:treat, giver: user)}

    before do 
      login_as(user)
    end

    it "can edit Treats they've given" do
      visit root_path
      click_button 'Edit'
      fill_in 'Name', with: 'A new name'
      fill_in 'Content', with: 'A new content'
      click_button "Update Treat"
      expect(page).to have_text("Treat was edited successfully")
    end
  end 
end

Here we replace the default creation of a "giver" user with the specific user returned by user method defined in the let block. This ensures that user == treat.giver so that your test can succeed.

CodePudding user response:

The build method in FactoryBot just creates an instance of the model in memory. You want to use create which saves the record to the database.

require "rails_helper"
RSpec.describe "Treat management", type: :system do

  # use let/let! to setup test dependencies 
  let!(:treat) { create(:treat) }
  # use create instead of build
  let(:user) { create(:user) }

  before do
    login_as(user)
    driven_by(:selenium_chrome_headless)
  end

  it "enables me to edit treats" do
    visit root_path
    click_button 'Edit'
    fill_in 'Name', with: 'A new name'
    fill_in 'Content', with: 'A new content'
    click_button "Update Treat"
    expect(page).to have_text("Treat was edited successfully")
  end
end
  • Related