-
Notifications
You must be signed in to change notification settings - Fork 479
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
Consistently Persist PD FactoryBot Records in v5 #51341
Conversation
In preparation for an update to FactoryBot 5, which fixes some unintuitive behavior when `build`ing objects with descendents. Specifically, the `:pd_teacher_application` factory calls `build :pd_teacher_application_hash_common` which in turn calls `association :school`; right now, even though the Hash is being called with `build` rather than `create`, that `School` object still gets persisted. In fact, that behavior is *required* since we pass the `id` of the `School` object upstream to the `:pd_teacher_application` factory which as part of its own persistence logic expects its `school_id` attribute to reference a persisted object. Startingin FactoryBot 5, the default association strategy will now be inherited from the parent factory, so `build`ing a hash will `build` but not persist the associated `School` object. In most cases this is probably desirable, but here we are relying on the old strange behavior and so now need to explicitly specify that we always want to persist the associated `School` object. https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md#build-strategies-1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for doing this, Elijah!
Upon further investigation, I found one more place where we need to update our use of School factories for FactoryBot v5, and learned about the potential for several more. Un-marking this PR as ready for review until I'm confident I've tracked down all of 'em |
pd_teacher_application_hash_common
Factory…s to be persisted
…so that its associated records are also persisted and able to be validated
@@ -178,7 +178,13 @@ class Api::V1::Pd::EnrollmentFlatAttendanceSerializerTest < ::ActionController:: | |||
end | |||
|
|||
test 'extract school and teacher info when they are empty' do | |||
enrollment = build :pd_enrollment, school_info: (build :school_info_us) | |||
enrollment = build( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using create
on line 168 but build
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it this case the existing code cares about the school_info
object being built but not created, whereas the other one doesn't have any such restrictions. And also because saving a Rails object will also save its associated records; using create
here will also attempt to persist school_info
enrollment = build :pd_enrollment, school_info: (build :school_info_us) | ||
enrollment = build( | ||
:pd_enrollment, | ||
# Don't persist school info; it's too empty to pass validation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by "it's too empty to pass validation"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is explicitly calling build :school_info_us
rather than just using the default school_info
factory because the default factory also creates associated school data and other stuff that's required for the model to pass validation, but this test explicitly wants to check the serializer behavior when school and teacher info is empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, makes sense. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moving from build
to create
when possible makes sense to me!
In preparation for an update to FactoryBot 5, which fixes some unintuitive behavior when
build
ing objects with descendants. Specifically, before, when creating records withbuild
, any associated records created as a result would unexpectedly be persisted withcreate
rather than just initialized withbuild
.In fact, for the
School
model specifically, that behavior is required, in a couple of ways: in the:pd_teacher_application
factory, we pass theid
of theSchool
object upstream to the:pd_teacher_application
factory which as part of its own persistence logic expects itsschool_id
attribute to reference a persisted object. And in the School model itself, because it doesn't use a standard Rails auto-incrementing integer id, we manually assign an id based on the latest persisted value; that means that we always want to persist any built School record before building another one, or we'll get anActiveRecord::RecordNotUnique
error.Starting in FactoryBot 5, the default association strategy will now be inherited from the parent factory, so
build
ing a hash willbuild
but not persist the associatedSchool
object. In most cases this is probably desirable, but here we are relying on the old strange behavior and so now need to explicitly specify that we always want to persist the associatedSchool
object.In a couple other places, we were implicitly (and I think accidentally) depending on this behavior to allow us to
build
and then validate records which require their associated records to be persisted in order to pass validation. In those cases, the fix was either tocreate
rather thanbuild
the original record in the first place, or to explicitlycreate
the associated records we want to be persisted.Links
Testing story
Without this change, we get the following error when trying to run tests on FactoryBot 5:
With this change, the test passes.
We also had two tests that were just validating internal FactoryBot persistence logic. One of them is no longer accurate because we actually now are persisting some of these dependencies consistently, and the other one is just a bit unnecessary. Remove them both.
Follow-up Work
Update FactoryBot to v5