Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FactoryBot::InvalidFactoryError: When assigning attributes, you must pass a hash as an argument #44

Closed
joshRpowell opened this issue Nov 5, 2019 · 4 comments

Comments

@joshRpowell
Copy link

joshRpowell commented Nov 5, 2019

Thanks for the great gem! First time user.

Implementation works well except when I run my specs I get the error noted above after switching my attributes from :jsonb to store_model. Nothing complex about the app/implementation.

Any thoughts on how to best handle?

class WorkerOption < ApplicationRecord
  # attribute :options, :jsonb, default: {}
  # attribute :data, :jsonb, default: {}
  attribute :data, WoCases.to_type
  attribute :options, Options.to_type
end

schema.rb

  create_table "worker_options", id: :serial, force: :cascade do |t|
    t.jsonb "options", default: "{}", null: false
    t.jsonb "data", default: "{}", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

running rspec

An error occurred in a `before(:suite)` hook.
Failure/Error: FactoryBot.lint

FactoryBot::InvalidFactoryError:
  The following factories are invalid:

  * worker_option - When assigning attributes, you must pass a hash as an argument. (ArgumentError)
# /Users/jrpowell/.gem/ruby/2.6.5/gems/factory_bot-5.1.1/lib/factory_bot/linter.rb:13:in `lint!'
# /Users/jrpowell/.gem/ruby/2.6.5/gems/factory_bot-5.1.1/lib/factory_bot.rb:74:in `lint'
# ./spec/support/init/factory_bot_rails.rb:21:in `block (2 levels) in <top (required)>'

/spec/support/init/factory_bot_rails.rb

RSpec.configure do |config|
  # Allows the use of "create :user" as a shorthand for "FactoryBot.create :user".
  config.include FactoryBot::Syntax::Methods

  config.before(:suite) do
    # This forces FactoryBot to recognize changes in factories.  It's useful for process forking with spring & spork.
    FactoryBot.reload

    begin
      # Prep DatabaseCleaner because the call to FactoryBot.lint below will leave models in the database.
      DatabaseCleaner.start
      # Ensure factories generate valid objects
      FactoryBot.lint
    ensure
      # Clean the database because FactoryBot.lint left models in database.
      DatabaseCleaner.clean
    end
  end
end

factories/worker_option.rb

FactoryBot.define do
  factory :worker_option, class: 'WorkerOption' do
    data { { "uri" => 'http://localhost:3000/api/v1/work_flows/1/case_machines/1/cases' } }

    trait :valid_options do
      options { { "id" => 666, "number" => "NUMBER", "receipt_number" => "RECEIPT_NUMBER" } }
    end
  end
end
@DmitryTsepelev
Copy link
Owner

Hi @joshRpowell! Thanks for the report, I'll do my best to take a look at the issue later this week

@DmitryTsepelev
Copy link
Owner

@joshRpowell I was able to reproduce it! The problem is that in schema you set up "{}" as a default, and then change default to {} in the attribute declaration. When replacing :jsonb with store models you removed attribute-level override, so the app crashes, when at least one jsonb field gets default string value. There are two options you have:

  1. bring back defaults in attributes definition
  2. change schema to use {} instead of "{}"

Here is a full example (save it to file, run createdb railstestdb and then execute the file with Ruby):

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  gem "rails"
  gem "pg"
  gem "factory_bot_rails"
  gem "store_model"
end

require "active_record"
require "action_controller/railtie"

ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "railstestdb")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  enable_extension "plpgsql"

  create_table "worker_options", id: :serial, force: :cascade do |t|
    t.jsonb "options", default: "{}", null: false
    t.jsonb "data", default: "{}", null: false
    # Option 1:
    # t.jsonb "options", default: {}, null: false
    # t.jsonb "data", default: {}, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end
end

class WoCases
  include StoreModel::Model

  attribute :uri, :string
end

class Options
  include StoreModel::Model

  attribute :id, :integer
  attribute :number, :string
  attribute :receipt_number, :string
end

class WorkerOption < ActiveRecord::Base
  attribute :data, WoCases.to_type
  attribute :options, Options.to_type

  # Option 2:
  # attribute :data, WoCases.to_type, default: {}
  # attribute :options, Options.to_type, default: {}
end

FactoryBot.define do
  factory :worker_option, class: 'WorkerOption' do
    data { { "uri" => 'http://localhost:3000/api/v1/work_flows/1/case_machines/1/cases' } }

    trait :valid_options do
      options { { "id" => 666, "number" => "NUMBER", "receipt_number" => "RECEIPT_NUMBER" } }
    end
  end
end

puts FactoryBot.create(:worker_option).inspect
# The following line always works, because all the attributes are set up!
puts FactoryBot.create(:worker_option, :valid_options).inspect

@DmitryTsepelev
Copy link
Owner

I'm closing this, please let me know if you need any help here, and I'll reopen it 🙂

@joshRpowell
Copy link
Author

@DmitryTsepelev Thanks for the prompt response. I made the recommended adjustments and it resolved my issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants