0

FactoryGirl 4.5.0

How do I change the structure of the passed attributes? For example, when working with a large set of existing tests, when serialising existing fields it is desirable for the factory to use the old attributes to make the new models.

Explanation

Given the model Emerald:

# app/models/emerald.rb
class Emerald < ActiveRecord::Base
  validates_presence_of :name, :size, :shinyness, :color
end

The factory for Emerald:

# spec/factories/emerald_spec.rb
FactoryGirl.define do
  factory :emerald do
    sequence(:name) {|n| "Emerald #{n} name"}
    size: 12
    shinyness: 34
    color: 'Dark Green'
  end
end

And many rspec tests which do something like:

let(:shiny_emerald) { create(:emerald, attributes: { name: 'Shiny', shinyness: 99 })}
let(:big_emerald)   { create(:emerald, attributes: { name: 'Big', size: 888 })}

When serialising some fields on Emerald we get

# app/models/emerald.rb
class Emerald < ActiveRecord::Base

  serialize :emerald_properties, Hash

  validates_presence_of :name
  validates :emerald_properties, emerald_properties: true # This is a custom validator

  def size
    emerald_properties[:size]
  end
  def size=(value)
    emerald_properties[:size] = value
  end
  def shinyness
    emerald_properties[:shinyness]
  end
  def shinyness=(value)
    emerald_properties[:shinyness] = value
  end
  def colour
    emerald_properties[:colour]
  end
  def colour=(value)
    emerald_properties[:colour] = value
  end
end

"Why don't you *do it a different way which might be possible in the limited scope of this example but doesn't apply to any person who might search for this*" aside, what should be done in the factory to allow the existing tests to remain unchanged?

The model still allows you to emerald.size instead of forcing the use of emerald.emerald_properties[:size], I am aiming to achieve the same thing for wielding the factory.

It is a lot of work to do:

let(:big_emerald) { create(:emerald, attributes: { name: 'Big', emerald_properties{ size: 888 }})}

everywhere, aiming to keep

let(:big_emerald) { create(:emerald, attributes: { name: 'Big', size: 888 })}

and adjust it inside the factory.

So far I have:

FactoryGirl.define do
  factory :emerald do
    sequence(:name) {|n| "Emerald #{n} name"}
    emerald_properties ({ {size: 12, shinyness: 34, color: 'Dark Green'}.merge(attributes.slice(*[:size, :shinyness, :color])) })
  end
end

but it does not work

xxjjnn
  • 14,591
  • 19
  • 61
  • 94

1 Answers1

0

Its only necessary to 'double bracket' when not doing inline.

A misunderstanding of How to define an array / hash in Factory Girl?

# valid
favorites {{
  "PETC" => "http://www.petc.org"
}}
# invalid
favorites {
  "PETC" => "http://www.petc.org"
}
# invalid
favorites {{ "PETC" => "http://www.petc.org" }}
# valid
favorites { "PETC" => "http://www.petc.org" }

So the solution works after removing unused braces:

FactoryGirl.define do
  factory :emerald do
    sequence(:name) {|n| "Emerald #{n} name"}
    emerald_properties { {size: 12, shinyness: 34, color: 'Dark Green'}.merge(attributes.slice(*[:size, :shinyness, :color])) }
  end
end

also instead of unnecessary {} they were unnecessary ()

Community
  • 1
  • 1
xxjjnn
  • 14,591
  • 19
  • 61
  • 94