Shairyar
Shairyar

Reputation: 3356

Rails 5 - RSpec with FactoryGirl giving error wrong number of arguments

This is the first time I am writing test cases on a rails project which is using RSpec and FactoryGirl

When I run the test case i get the following error

wrong number of arguments (given 0, expected 1)

I have gone through other posts at stack over flow and they are not much helpful in my case.

What I have tried

I am writing a test case on a Model which is called ImportFeed and it looks something like as following

class ImportFeed < ApplicationRecord
  belongs_to :staffroom
  belongs_to :user,  optional: true   # We don't have to have a user

  validates_presence_of  :url, :feed_type
  validates :enabled,     presence: true, allow_blank: true

  def initialize(params)
    super(params)
    self.enabled = false if self.enabled.blank?
    self.default_radius = DEFAULT_RADIUS  if self.default_radius.blank?
    self.default_days = DAYS_DEFAULT if self.default_days.blank?
  end
end

This is what my test case looks like

require 'rails_helper'

describe JobImporters::JoraJobImporter, '.run' do

  it 'should create an instance of ImportFeed' do
    feed = ImportFeed::new FactoryGirl.create(:import_feed, :import1)
    expect(feed).to be_a ImportFeed
  end
end

This is the factory

FactoryGirl.define do

  factory :import_feed do

    trait :import1 do
      enabled true
      feed_type 'example'
      staffroom_id 7526
      url Faker::Internet::url
    end
  end
end

When I run this I get the error mentioned at the beginning of this question,

If I pass the data to the test case without FactoryGirl then my test case works and passes for example if I replace

feed = ImportFeed::new FactoryGirl.create(:import_feed, :import1)

with

feed = ImportFeed::new enabled: true, staffroom_id: 7526, feed_type: 'example', url: Faker::Internet::url

the test case passes.

I will really appreciate if someone can point to me what am I doing wrong here.

Upvotes: 1

Views: 1101

Answers (2)

fongfan999
fongfan999

Reputation: 2624

Because you're overriding initialize method, so you got unexpected exception.

Don't override initialize on ActiveRecord objects ActiveRecord::Base doesn't always use new to create objects, so initialize might not be called. [link]

In order to solve your problem, you should set your attributes in callback instead

class ImportFeed < ApplicationRecord
  # ...
  after_initialize :set_my_attributes

  private

  def set_my_attributes
    self.enabled = false if self.enabled.blank?
    self.default_radius = DEFAULT_RADIUS  if self.default_radius.blank?
    self.default_days = DAYS_DEFAULT if self.default_days.blank?
  end
end

One more thing:

You're testing creating an instance of ImportFeed functionality, so you should either pass params to new or create methods to test it, but you pass an instance of ImportFeed to it (from FactoryGirl).

According to the docs, ActiveRecord#new accepts Hash only (the default argument is {} if you don't pass anything).

If you pass an object to it, you'll get ArgumentError exception along with "When assigning attributes, you must pass a hash as an argument" message

def assign_attributes(new_attributes)
  if !new_attributes.respond_to?(:stringify_keys)
    raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
  end
  return if new_attributes.empty?

  attributes = new_attributes.stringify_keys
  _assign_attributes(sanitize_for_mass_assignment(attributes))
end

Upvotes: 2

Deepesh
Deepesh

Reputation: 6398

I think you are just using the initialize method to change the values according to the conditions:

def initialize(params)
  super(params)
  self.enabled = false if self.enabled.blank?
  self.default_radius = DEFAULT_RADIUS  if self.default_radius.blank?
  self.default_days = DAYS_DEFAULT if self.default_days.blank?
end

You should not override it (my suggestion) as it may break many things. So instead you may change the values on a callback (before_validation, before_save, before_create, after_initialize whichever suits you) like this:

before_create :set_default_radius, if: proc { |feed| feed.default_radius.blank? }

def set_default_radius
  self.default_radius = DEFAULT_RADIUS
end

And the best way to do this is having the default value in database itself. You can define that in migration:

def up
  change_column :import_feeds, :default_radius, :integer, default: 0
end

def down
  change_column :import_feeds, :default_radius, :integer, default: nil
end

So if the value is not defined it will always set to the default value mentioned in the migration file.

Also you may have a read of several question related to this which has some very good answers and explanation:

How to override "new" method for a rails model

Why is overriding ActiveRecord::Base.initialize wrong?

Overriding ApplicationRecord initialize, bad idea?

Upvotes: 1

Related Questions