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

Add definition names to default trait key errors #1421

Merged
merged 2 commits into from
Jul 10, 2020

Conversation

composerinteralia
Copy link
Collaborator

Closes #1222

Before this commit, referencing a trait that didn't exist would raise a
generic KeyError: Trait not registered: "trait_name". This can
sometime make it difficult to know where exactly the error is coming
from. The fact that implicitly declared associations and sequences will
fall back to implicit traits if they can't be found compounds this
problem. If various associations, sequences, or traits share the same
name, the hunt for the error can take some time.

With this commit we include the factory or trait name (i.e. the
definition name) in the error message to help identify where the
problematic trait originated.

Because trait lookup relies on a more generic class that raises a
KeyError for missing keys, we rescue that error, then construct a new
error with our custom message using the original error's message and
backtrace.

Closes #1222

Before this commit, referencing a trait that didn't exist would raise a
generic `KeyError: Trait not registered: "trait_name"`. This can
sometime make it difficult to know where exactly the error is coming
from. The fact that implicitly declared associations and sequences will
fall back to implicit traits if they can't be found compounds this
problem. If various associations, sequences, or traits share the same
name, the hunt for the error can take some time.

With this commit we include the factory or trait name (i.e. the
definition name) in the error message to help identify where the
problematic trait originated.

Because trait lookup relies on a more generic class that raises a
`KeyError` for missing keys, we rescue that error, then construct a new
error with our custom message using the original error's message and
backtrace.
expect { FactoryBot.build(:user) }.to raise_error(
KeyError,
'Trait not registered: "inaccessible_trait" ' \
'(referenced within "user" definition)'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this multi-line string formatted by Standard?

Would a HEREDOC be a viable alternative?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what you were thinking?

        <<~MSG.strip
          Trait not registered: "inaccessible_trait" (referenced within "user" definition)
        MSG

At that point I would prefer:

        'Trait not registered: "inaccessible_trait" (referenced within "user" definition)'

I didn't write it that way because the line was getting a bit long. But I could be convinced that it is better for readability. What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with the string all on one line for now, since it is a little bit easier to read.


def append_definition_name_to_error(error)
message = "#{error.message} (referenced within \"#{name}\" definition)"
error.class.new(message).tap do |new_error|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clever and effective way to re-raise the underlying KeyError.

Is there a downside to raising an error class that FactoryBot declares, instead of KeyError from Ruby's standard library?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sean beat me to this. I don't think it's a bad idea necessarily to use a KeyError but a FactoryBot error class would be sweet considering this seems like a FactoryBot specific error

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we consider the originally raised KeyError part of a factory's public interface? Is re-raising the KeyError motivated by backwards compatibility?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason we are using a KeyError rather than a custom FactoryBot error class is because KeyErrors come with Did you mean? suggestions (see b86ec13).

But you are reminding me that I should probably add a test around that case. The Did you mean? message is meant to come at the end, so I might have to insert the custom bit here into the middle somewhere.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I modified this to handle the Did you mean? message.

Copy link
Collaborator Author

@composerinteralia composerinteralia Jul 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I think about this more, I think we could probably update the FactoryBot::Registry to raise a custom factory_bot error instead of a KeyError, since by the time we build the new error the Did you mean? message is already populated. But I think that is beyond the scope of this PR. @seanpdoyle or @das3in would either of you be interested in tying that out and making a PR?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I'll take a stab at that this weekend.

@composerinteralia composerinteralia force-pushed the add-definition-to-trait-key-error branch from 6025cd5 to cd7a228 Compare July 10, 2020 15:38
@composerinteralia composerinteralia merged commit d05a9a3 into master Jul 10, 2020
@composerinteralia composerinteralia deleted the add-definition-to-trait-key-error branch July 10, 2020 15:53
composerinteralia added a commit to composerinteralia/factory_bot that referenced this pull request Oct 23, 2020
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

Successfully merging this pull request may close these issues.

Include factory name in "Trait not registered" error message
3 participants