thoughtbot / factory_bot

A library for setting up Ruby objects as test data.

Home Page:https://thoughtbot.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for default attributes for JSON columns

johansenja opened this issue · comments

Problem this feature will solve

Currently, I can create a factory like this:

FactoryBot.define do
  factory :a do
    my_json_column do
      {
        "a" => 1,
        "b" => 2,
        "c" => 3,
      }
    end
  end
end

and use it like this:

create :a, my_json_column: { "a" => 2, "b" => 3, "c" => 4 }

which means I have to specify every key which I want to appear in my_json_column.

Desired solution

I'm wondering if it would be possible to do something like this:

FactoryBot.define do
  factory :a do
    json :my_json_column do # or maybe hash? or json_factory?
      {
        "a" => 1,
        "b" => 2,
        "c" => 3,
      }
    end
  end
end

which would mean that I could only pass in the properties I cared about, and the rest would just be populated from the definition in the factory (similar to regular properties in factory).

a = create :a, my_json_column: { "a" => 2 }
a.my_json_column # { "a" => 2, "b" => 2, "c" => 3 }

Alternatives considered

I've considered doing something like this (or similar) as a workaround, though it doesn't seem that clean, especially if you are doing it a fair bit.

atrs = attributes_for :a
create :a, my_json_column: { **atrs.my_json_column, "a" => 2 }

But if anyone has any other suggestions then I'm all ears!

Additional context

Example use case:

Imagine you have a config json column, which just stores some flexible key-value pairs for a model; if you have some sort of validation on this, which checks for various keys, it would be cumbersome to specify all of them every time you have a test which just tests a single one of those keys.

Thanks!

Hi @johansenja thanks for the suggestion. We have some questions about how you envision the API for this new feature to work, but on the meantime, can we suggest using transient attributes to achieve a similar result? We'll attach a repro script so you can see it in action, but the main idea is to use a default set of key-value pairs for the json hash you wish to override and you can merge it with the overridden input, like:

FactoryBot.define do
  DEFAULT_OPTIONS = {"a" => 1}
  factory :user do
    transient do
      override_options {{}}
    end
    sequence(:name) { |sequence| "name #{sequence}" }

    after(:build) do |user, evaluator|
      puts evaluator.override_options
      user.options = DEFAULT_OPTIONS.merge(evaluator.override_options)
    end
  end
end

class FactoryBotTest < Minitest::Test
  def test_attributes
    user = FactoryBot.create(:user, override_options: {"a" => 3, "b" => 2})
    assert_equal(user.options, {"a" => 3, "b" => 2})
  end
end

With this you can have the syntactic sugar you wish to support default values on the json column and overrides certainly work! Let us know what you think, and please provide the reproduction script with the proposal, even if it doesn't work it helps envision a strategy.

repro.rb.txt