thoughtbot / factory_bot_rails

Factory Bot ♥ Rails

Home Page:https://thoughtbot.com/services/ruby-on-rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

version 5.0.0 association creation

chevinbrown opened this issue · comments

This is honestly probably the way it should work (I'm assuming it's somewhat related d320202#diff-e79a60dc6b85309ae70a6ea8261eaf95R26)

When building a record with an association, the associations are not created first.

Quick example:

FactoryBot.define do
  factory :user do
    association :doggie
  end
end

In ~4.11 build :user would create the doggie association.
In ~5.0.0 build :user does not yet create the association.

Is this expected?

I prefer it b/c it's more explicit, but it does seem to be breaking (if indeed I'm on the right track). Any insights?

Thanks for opening this issue. This was an intentional change; we changed the default value for use_parent_strategy to true in thoughtbot/factory_bot@d0208ed. It is indeed a breaking a change. I would be curious to know how many failures this caused in your test suite, and how long it took you to figure out what was going on.

Just a few failures, I discovered/reported/fixed in under an hour, so it's pretty minimal.

Was that change missed in the changelog? We got a dependabot PR today to update us from 4.11 -> 5.0

https://github.com/thoughtbot/factory_bot/blob/806ec9f6fe071f3576aa28c804bacfc7eefc790e/NEWS#L3

But maybe it didn't show up in the dependabot PR because it is in the factory_bot NEWS file, not factory_bot_rails?

Let's leave this issue open, since I'm sure others will run into this as well.

Also, I should mention that it is possible to revert to the old behavior by setting FactoryBot.use_parent_strategy = false, but I would love to know if anyone ends up needing to do that. Ideally we could deprecate the old behavior at some point, but I don't want to make it impossible for people to update.

Yeah, that's probably the reason I missed it.

Thanks for the quick response.

Annecdotally, (not to bikeshed!) I greatly prefer an additional setup step instead of a rails-specific gem...Feel free to email me if there are discussion points I need to understand there.

Maybe I should just throw FactoryBot.find_definitions in my test_hepler.rb 😉

I agree--this "new" behavior is best!

Annecdotally, (not to bikeshed!) I greatly prefer an additional setup step instead of a rails-specific gem...Feel free to email me if there are discussion points I need to understand there.

factory_bot_rails does include a few extra features beyond automatically loading definitions--mostly notably the generator--but if you are not using those I think using factory_bot and calling FactoryBot.find_definitions yourself is totally reasonable.

@composerinteralia totes. +1 for your responsiveness. Thanks for factory bot! (and ex_machina!)

So if changing the default back is not the recommended way to resolve issues caused by this change then can I ask what is? I can find documentation explaining how this causes behaviour to change, but not how to adapt tests that need to build objects whose associations have been fully created...

If it helps to have an example then consider https://github.com/openstreetmap/openstreetmap-website/blob/master/test/models/old_node_test.rb#L9 which creates an object and then tests it's validity to check that the validations are working. We don't want to try and save it because it might not be valid (indeed some tests do use invalid objects) but we do need the associated objects to be save or their ids won't be set and validation will fail because of that.

@tomhughes the most simple case in my experience:

# old
# fails b/c of some nonexistent relationship
build(:old_node, :latitude => 90 * OldNode::SCALE)

# new
build(:old_node, :latitude => 90 * OldNode::SCALE, dependent_association: create(:other_node))

You just have to be more explicit in the setup.

I generally find this to be a better pattern anyway--take more time to do slightly more verbose setup blocks will help serve future me when I come back to the test if it's failing.

Thanks. I went a slightly different way in the end and made the tests check for the validation they are actually interested in which should be more robust - some of them were effectively silently not doing what was intended as a result of this change combined with them being too generic.

Just for additional context on this issue: The upgrade is breaking a quarter of spec suites according to meta analysis by depend-a-bot
https://dependabot.com/compatibility-score/?dependency-name=factory_bot_rails&package-manager=bundler&previous-version=4.11.1&new-version=5.0.0

@composerinteralia any reason thoughtbot prefers NEWS file over something like CHANGELOG?

commented

Thanks for opening this issue. This was an intentional change; we changed the default value for use_parent_strategy to true in thoughtbot/factory_bot@d0208ed. It is indeed a breaking a change. I would be curious to know how many failures this caused in your test suite, and how long it took you to figure out what was going on.

This took us a good couple of hours to figure out, and failed on the linting step. I didn't immediately realise it had to do with a factory_bot upgrade (and we're not using rails), so it took me a while to find the documented issues.

@pboling thank you for that information. That makes sense, since changing the default value for FactoryBot.use_parent_strategy was an intentional breaking change. (copied from thoughtbot/factory_bot#1255)

@chevinbrown I haven't thought much about it, but https://www.gnu.org/prep/standards/standards.html#NEWS-File seems to indicate that NEWS files are for user-visible changes, and change logs include all the details of every internal change. Our NEWS files do focus on user-visible changes.

@jrgns thanks for that feedback. I am thinking about maybe writing up a blog post with the details of why we changed the default for FactoryBot.use_parent_strategy, and how to update. I will be sure to post it here when it is ready.

@tomhughes I really like the changes you made in openstreetmap/openstreetmap-website@f7694a9. Let me know if you run into any other problems.

Thanks for keeping the issue open and findable!
Using FactoryBot.use_parent_strategy = false seems to make the tests pass again.
We have about 25 tests failing, which is not too many, but some of the fixes wouldn't be very straight forward. I wish it would have been announced a bit more explicitly that the default value is to blame surrounding how :build behaves. It took some hours to get here.

Thanks @erikraudsepp. If at some point you do tackle the failing tests, I would be curious which fixes were not straightforward.

I wish it would have been announced a bit more explicitly that the default value is to blame surrounding how :build behaves

I had thought https://github.com/thoughtbot/factory_bot/blob/806ec9f6fe071f3576aa28c804bacfc7eefc790e/NEWS#L3-L4 would be explicit enough. For the future, is there something else that would have helped you?

@composerinteralia re-reading this thread, I'd advocate for using CHANGELOG.md for these things. I feel that the community at large would benefit b/c things like dependabot parse that and that's usually my go-to location for...changes. 😄
It's also somewhat a general standard and understood by the community.
Maybe NEWS could be used to forecast issues/changes (like the factory-girl -> factory-bot change) and CHANGELOG.md could contain the actual API changes?

After re-rereading...
Is there just impedance b/t the rails gem changes and this one?

@chevinbrown I think dependabot does recognize the NEWS file. I think your updated comment is right--people using factory_bot_rails might not have seen the changes listed in factory_bot's NEWS file.

@composerinteralia thank you for your response.

In the test we were using:

data_stream = build(:data_stream, data_streamable: @custom_field)

Which in the factory is defined as:

  factory :data_stream, traits: %i[ownable] do
    name { "Dæta Strëam #{Faker::ElderScrolls.creature.pluralize}" }
    before(:create) do |obj, _|
      StampPermission.run!(object: obj, person: obj.owner)
    end
  end

At first I thought that I would just need to change the callback before(:create) to after(:build). But after a few tries, I also noticed that now I need to pass in the owner.
data_stream = build(:data_stream, owner: @owner, data_streamable: @custom_field)
As the errors it gave were identical, I thought that the callback still didn't get run, although I changed it to after(:build).
I think the rest of the changes are identical to
openstreetmap/openstreetmap-website@f7694a9

Now after being more familiar with how factory_bot works, I think the changelog message is enough. Using factory_bot has been intuitive enough that I haven't had any need so far to familiarize myself with the readme and "use_parent_strategy" didn't click at all.

It took me about 15 minutes to track down the issue in my app and append , strategy: :create to the association call in the do block for the affected factories.

I haven't had time to write a blog post about this, but if anybody is curious about the motivation behind this change I tried to dump all of my thoughts into the commit message: thoughtbot/factory_bot@d0208ed

Thanks, @composerinteralia that's super helpful.

Ruby 2.6.3
Rails 5.2.3

When upgrading to version 5, I am getting the can't modify frozen Hash error. I can identify two types of failures:
build / save scenario

describe '#publish_to_twitter' do
  context 'on answer create' do
    let(:answer) { build(:answer) }

    it "publish to twitter" do
      expect(answer).to receive(:publish_to_twitter)
      answer.save
    end
end

shoulda-matcher validate_uniqueness_of with scope
it { is_expected.to validate_uniqueness_of(:voter_id).scoped_to([:voteable_type, :voteable_id, :voter_type]) }
If I set FactoryBot.use_parent_strategy = false, the specs pass as before the upgrade.

@andreierdoss are you able to provide a reproduction script in this format? That would make it easier to debug what is going on in your specific case. can't modify frozen Hash doesn't ring any bells for me.

This change has been out for 10 months now, so I am going to go ahead and close this issue now. Thanks everyone for your help!