Search code examples
ruby-on-railsrspecdevisecapybararspec-rails

RSpec/Capybara test failing with no method error for model setter


Short Version

I have added a company model to my rails app which is in the devise user registration view using accepts_nested_attributes on the user model. The setup works when I run through it manually but when I execute the test it fails with:

Failure/Error: click_button 'Sign up'
     NoMethodError:
       undefined method `owner_id=' for #<Company:0x007fe2411e17b8>

How can I fix this error? I am not sure what it is telling me.

The test code which is in spec/features:

authentication_flows_spec.rb

it "signs me up" do
  visit new_user_registration_path
  fill_in 'user[email]', with: @new_user[:email]
  fill_in 'user[password]', with: @new_user[:password]
  fill_in 'user[password_confirmation]', with: @new_user[:password]
  fill_in 'user[company_attributes][name]', with: @new_user[:company_name]
  click_button 'Sign up'
  expect(current_path).to eq(dashboard_path)
end

Long Version

Recently I have added a Company model to my app which has two relationships. One to say it has_many users and one to say it belongs_to an owner (which is a existing user).

Company Model

class Company < ActiveRecord::Base
  belongs_to :owner, class_name: 'User', foreign_key: 'owner_id'
  has_many :users
end

The company is created in the devise user registration by telling my user model to accepts_nested_attributes_for the company.

User Model

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  belongs_to :company
  accepts_nested_attributes_for :company
end

Devise User Registration View

.section.first.dark-grey.p-b-20
  .container
    .grid
      .col-md-6.col-md-offset-3
        %h2.grid-title Sign up
        = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
          = devise_error_messages!
          .form-row
            = f.email_field :email, autofocus: true, class: "form-control", placeholder: "email address"
          .form-row
            = f.password_field :password, autocomplete: "off", class: "form-control", placeholder: "password"
          .form-row
            = f.password_field :password_confirmation, autocomplete: "off", class: "form-control", placeholder: "confirm password"
          = f.fields_for :company_attributes do |ff|
            .form-row
              = ff.text_field :name, class: "form-control", placeholder: "company name"
          .form-row
            = f.submit "Sign up", class: "btn btn-primary btn-cons"
        = render "devise/shared/links"

Finally I override the devise user registrations controller to add the logic to set the owner of the company

Overriden Devise Registrations Controller

class RegistrationsController < Devise::RegistrationsController
  before_filter :configure_permitted_parameters

    #GET /users/sign_up
    def new
      # Override Devise default behaviour and create a company as well
      build_resource({})
      resource.build_company
      respond_with self.resource
    end

    #POST /users/sign_up
    def create
      # Let devise create user and company using nested attributes
      super 
      # Set the owner of the newly created company as new user
      company = resource.company
      company.owner_id = resource.id
      company_saved = company.save
      if company_saved == false
        resource.destroy
        company.destroy
        return new_user_registration_path
      end  
    end

  protected

  def configure_permitted_parameters
      devise_parameter_sanitizer.for(:sign_up) { |u|
        u.permit(:email, :password, :password_confirmation, :company_attributes => :name)
      }
    end
end

Solution

  • On your Reddit post, adding attr_accessor :owner_id seemed to resolve the problem. That suggests that the test db schema really is the problem as @HermanHiddema suggests. Instead of db:test:prepare (removed in 4.1), you could do RAILS_ENV=test rake db:schema:load or similar to drop the existing schema and recreate from scratch.

    maintain_test_schema! appears to only load the schema if you the schema version is different from the one in the DB. So if you modified migrations that had already run (as one example), it wouldn't detect that the test schema needs to be reset.