Anders Anders - 4 months ago 20
Ruby Question

Capybara + FactoryGirl, set relationship in form

I'm a newcomer when it comes to both

Capybara
FactoryGirl
, In my Rails app I have a relationship that looks like this:

# App.rb
belong_to :plan

# Plan.rb
has_many :apps


Each app must have a plan, in my App.rb model I do it like this:
before_save :set_default_plan, on: :create
.

I want to test that app creation works, using Capybara integration tests. I currently have a test that looks like this:

require "rails_helper"

include Warden::Test::Helpers
Warden.test_mode!

describe "adding apps" do

let(:user) { FactoryGirl.create(:user) }
before { login_as(user, scope: :user) }

it "allows a user to create an app" do
visit apps_path
fill_in "App name", with: "My App"
click_on "create_app_button"
visit apps_path
expect(page).to have_content("My App")
end
end


After I create apps I render this in my view:
#{app.plan.free_requests}
. If I run my tests with
bundle exec rspec
I currently get this error:

undefined method `free_requests' for nil:NilClass


In my app I also use FactoryGirl to test my models. I have the following (relevant) factories:

FactoryGirl.define do
factory :app do
name "Test"
[...]
association :plan, :factory => :plan
end
end

FactoryGirl.define do
factory :plan do
name "Default"
[...]
end
end


I wonder how I should set ut my factories and test suite to make this test become a green one.

Can I either assign a plan to the app I'm creating somehow with Capybara, or can I create a default association / plan for my app with FactoryGirl. Is is there another approach? Thankful for all assistance.

Update

This is how my
set_default_plan
method looks:

# App.rb
def set_default_plan
if self.new_record?
plan = Plan.find_by_stripe_id("default_plan")
if plan.nil? == false
self.plan = plan
end
end
end

Answer

FactoryGirl really shouldn't have anything to do with "apps" or "plans" in your test, since you're running through your controllers create action, unless set_default_plan doesn't actually create a plan if none exist. If that is the case then you could use FactoryGirl to create the required plan like - FactoryGirl.create(:plan) in your before block

You should also specify that the plan is a required association (this is default in Rails 5 so if you're using that this may not be necessary) which will prevent your Apps from being created without a plan.

# App.rb
belongs_to :plan, required: true

Another thing to note is you should always check for confirmation after clicking a button that performs an action before visiting another page. This is because the result of clicking the button is not guaranteed to be synchronous so visiting another page immediately can kill the action request.

click_on "create_app_button"
expect(page).to have_content("App Created!!!")  # whatever text is shown on success
visit apps_path