Search code examples
rubyruby-on-rails-4factory-botrspec-railsnomethoderror

undefind method firstname= while using factory_girl and rspec tests


http://everydayrails.com/2012/03/19/testing-series-rspec-models-factory-girl.html# going off of this tutorial currently. i am adding on to another project that i started from the rails apps tutorial.

as i go to create my models specs contact_spec.rb

require 'spec_helper'

describe Contact do 
it "has a valid factory" do
    FactoryGirl.create(:contact).should be_valid
end
it "is invalid without a firstname"
it "is invalid without a lastname"
it "returns a contact's full name as a string"  
end

factories/contacts.rb

require 'faker'

FactoryGirl.define do
    factory :contact do |f|
    f.firstname { Faker::Name.first_name }
    f.lastname { Faker::Name.last_name }
    end
end

i run rspec and recieve this error:

    1) Contact has a valid factory
    Failure/Error: FactoryGirl.create(:contact).should be_valid
    NoMethodError:
    undefined method `firstname=' for #<Contact name: nil, email: nil, content: nil>
    # ./spec/models/contact_spec.rb:5:in `block (2 levels) in <top (required)>'
    Finished in 0.22028 seconds
    4 examples, 1 failure, 3 pending

here is my contact model

 class Contact < ActiveRecord::Base
  has_no_table

  column :name, :string
  column :email, :string
  column :content, :string

  validates_presence_of :name
  validates_presence_of :email
  validates_presence_of :content
  validates_format_of :email, :with => /\A[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}\z/i
  validates_length_of :content, :maximum => 500

  def update_spreadsheet
    connection = GoogleDrive.login(ENV["GMAIL_USERNAME"], ENV["GMAIL_PASSWORD"])
    ss = connection.spreadsheet_by_title('Learn-Rails-Example')
     if ss.nil?
      ss = connection.create_spreadsheet('Learn-Rails-Example')
    end
    ws = ss.worksheets[0]
    last_row = 1 + ws.num_rows
    ws[last_row, 1] = Time.new
    ws[last_row, 2] = self.name
    ws[last_row, 3] = self.email
    ws[last_row, 4] = self.content
    ws.save
  end

end

and my contactS_controller

class ContactsController < ApplicationController

  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(secure_params[:contact])
    if @contact.valid?
      @contact.update_spreadsheet
      UserMailer.contact_email(@contact).deliver
      flash[:notice] = "Message sent from #{@contact.name}."
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def secure_params
    params.require(:contact).permit(:name, :email, :content)
  end

end

i tried changing :name to be :firstname, and :lastname and ended up getting this new error

1) Contact has a valid factory
     Failure/Error: FactoryGirl.create(:contact).should be_valid
     ActiveRecord::RecordInvalid:
       Validation failed: Email can't be blank, Email is invalid, Content can't be blank
     # ./spec/models/contact_spec.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.22596 seconds
4 examples, 1 failure, 3 pending

Failed examples:

rspec ./spec/models/contact_spec.rb:4 # Contact has a valid factory

my guess is that i havent defined something right when i reference to :contact.

edit adding the Faker::Internet.email attr brought up a new error

 1) Contact has a valid factory
     Failure/Error: FactoryGirl.create(:contact).should be_valid
     ActiveRecord::RecordInvalid:
       Validation failed: Content can't be blank
     # ./spec/models/contact_spec.rb:5:in `block (2 levels) in <top (required)>'

so i comment out validates_presence_of :content, column :content, and validates_length_of :content and get this

 1) Contact has a valid factory
     Failure/Error: FactoryGirl.create(:contact).should be_valid
     ActiveRecord::Tableless::NoDatabase:
       Can't #create_record a Tableless object
     # ./spec/models/contact_spec.rb:5:in `block (2 levels) in <top (required)>'

so it seems that things are working as i apply them or take away, i apparently have lots of conflicts between the code i wrote following one tutorial and adding this method using rspec factory_girl and faker

edit 2 so after the addition of the email and content attr, i continued on with filling out the remaining specs for "invalid without first/lastname":

    describe Contact do 
            it "has a valid factory" do
        FactoryGirl.build(:contact).should be_valid
        end
        it "is invalid without a firstname" do 
            FactoryGirl.build(:contact, lastname: nil).should_not be_valid 
        end

         it "is invalid without a lastname" do 
            FactoryGirl.build(:contact, lastname: nil).should_not be_valid 
        end

        it "returns a contact's full name as a string"  
    end

and my tests passed with no errors:

  is invalid without a lastname
  has a valid factory
  is invalid without a firstname
  returns a contact's full name as a string (PENDING: Not yet implemented)

Pending:
  Contact returns a contact's full name as a string
    # Not yet implemented
    # ./spec/models/contact_spec.rb:15

Finished in 0.22146 seconds
4 examples, 0 failures, 1 pending

thank you sonnyhe2002 for getting me through this! much appreciated!


Solution

  • You need to add an email attr to your factory

    FactoryGirl.define do
      factory :contact do |f|
        f.firstname { Faker::Name.first_name }
        f.lastname { Faker::Name.last_name }
        f.email { Faker::Internet.email }
        f.content 'This is content'
      end
    end
    

    Since you are using has_no_table, I think factory cannot save it into the database.

    You should use 'build' instead of 'create'

    FactoryGirl.build(:contact).should be_valid