-
Notifications
You must be signed in to change notification settings - Fork 21.7k
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
Adds assert_error
and assert_no_error
#51774
base: main
Are you sure you want to change the base?
Conversation
da9a974
to
f7da59d
Compare
# Assertion that an active model has a specific invalid field | ||
# | ||
# assert_invalid :name, :blank, :user | ||
def assert_invalid(attribute, type, obj, msg = nil) |
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.
Thinking about this, having only assert_invalid
feels like it doesn't match the framework's other assertions. I think the framework should support assert_valid
and assert_not_valid
. What do you think about changing this PR to support those?
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.
That's a great idea, thank you for the feedback. I updated it and changed the name of the method to make it more descriptive so it's clear that it references errors in the model.
f7da59d
to
7480a6b
Compare
assert_invalid
assert_error
and assert_no_error
Does this work when the error is added as a string instead of symbol? |
# Assertion that an active model doesn't have an error on a field | ||
# | ||
# assert_no_error :name, :blank, :user | ||
def assert_no_error(attribute, type, obj, msg = nil) | ||
raise ArgumentError.new("#{obj.inspect} does not respond to #validate") unless obj.respond_to?(:validate) | ||
|
||
obj.validate | ||
msg = message(msg) { | ||
data = [attribute, type] | ||
"Expected %s to not be %s" % data | ||
} | ||
assert_empty obj.errors.where(attribute, type), msg | ||
end | ||
|
||
# Assertion that an active model has a specific error on a field | ||
# | ||
# assert_error :name, :blank, :user | ||
def assert_error(attribute, type, obj, msg = nil) | ||
raise ArgumentError.new("#{obj.inspect} does not respond to #validate") unless obj.respond_to?(:validate) | ||
|
||
obj.validate | ||
msg = message(msg) { | ||
data = [attribute, type] | ||
"Expected %s to be %s" % data | ||
} | ||
refute_empty obj.errors.where(attribute, type), msg | ||
end |
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.
To share some prior art in this space, thoughtbot/shoulda-matchers might be of interest. It is a 14 year old project that provides Active Model validation assertions (amongst other things).
Like this proposal, it also invokes #validate
on the object directly. Unlike this proposal, it names its matches in a way that communicates that it's invoking #validate
(for example, validates_presence_of
, validates_uniqueness_of
, etc.).
Callers of an assert_no_error
assertions might be surprised to learn that it's also invoking #validate
, since #validate
clears any errors present on the object before re-validating. Depending on the details of the test itself, that kind of reseting might un-do and remove errors added during that test.
As a caller, I might expect assert_no_error
to only make assertions about the errors available by the subject under test's #errors method.
If this proposal is deemed viable, what do you think about introducing both assert_valid
and assert_not_valid
assertions that invoke assert_no_error
and assert_error
(respectively)? That way, callers can benefit from the convenience of assert_valid
or assert_not_valid
while also being able to drop down to the finer-grained assert_error
or assert_no_error
.
I'm imagining something like:
# Assertion that an active model doesn't have an error on a field
#
# assert_no_error :name, :blank, :user
def assert_no_error(attribute, type, obj, msg = nil)
msg = message(msg) {
data = [attribute, type]
"Expected %s to not be %s" % data
}
assert_empty obj.errors.where(attribute, type), msg
end
# Assertion that an active model has a specific error on a field
#
# assert_error :name, :blank, :user
def assert_error(attribute, type, obj, msg = nil)
msg = message(msg) {
data = [attribute, type]
"Expected %s to be %s" % data
}
refute_empty obj.errors.where(attribute, type), msg
end
def assert_not_valid(attribute, type, obj, msg = nil)
raise ArgumentError.new("#{obj.inspect} does not respond to #validate") unless obj.respond_to?(:validate)
obj.validate
assert_error(attribute, type, object, msg)
end
def assert_valid(attribute, type, object, msg = nil)
raise ArgumentError.new("#{obj.inspect} does not respond to #validate") unless obj.respond_to?(:validate)
obj.validate
assert_empty obj.errors, msg
end
It's worth mentioning that assert_valid
doesn't exactly mean "there are no errors under this particular key", it means "there are no errors.
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.
Are there plans to support Active Model Validation Contexts?
When invoking the version of assert_no_error
that does not call #validate, it's up to the test code that precedes the assertion to validate with the proper context.
When invoking the assert_valid
and assert_not_valid
variations, there isn't a way to forward along any contextual information (like an :on
option). This is also true of the currently proposed assert_error
and assert_no_error
definitions that call #validate
directly.
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.
Callers of an assert_no_error assertions might be surprised to learn that it's also invoking #validate
Agreed, thanks for the feedback. I will go ahead and make those changes.
Regarding
Are there plans to support Active Model Validation Contexts?
This can be another type of assertion. However, the current change aims to focus on a model that went through the process of doing its validations.
PS: This is my first contribution ever to Rails and if it's better to ship a more defined feature then I am onboard with adding more stuff. LMK what's the approach generally taken. I am thinking this in terms of small deliverable.
192aebd
to
c7d35c1
Compare
0568062
to
3cde006
Compare
It does, since it's using added to make the assertions |
I'm fine being veto'd by someone else on Core but I don't like
|
If the name is generic enough like this, and it really only applies to ActiveModel objects, then it should only be available in something like |
@zzak, the problem with that approach is that then tests would have to inherit from something like |
fa2907d
to
648e297
Compare
fa2907d
to
655f8c5
Compare
Yes, I realized that after I made the comment, sorry was just thinking about it. Would you mind fixing the failing tests / lint errors?
|
1e29075
to
ee2e91e
Compare
ee2e91e
to
2454755
Compare
Introduces two new assertions, `assert_error_on` and `assert_no_error_on`, to simplify checking for specific validation errors on models. Example usage: - assert_error_on user, :name, :blank - assert_no_error_on user, :name, :blank This enhances test readability and makes validation testing more intuitive.
2454755
to
915e423
Compare
Motivation / Background
Ensuring the correctness of validations in Rails models is a fundamental aspect of robust testing. However, the current approach to such validations can often lead to cluttered and less readable test code. For instance, manually asserting validation errors requires multiple lines of code, detracting from the clarity of the test suite. This PR addresses this issue by introducing a streamlined solution that enhances readability and conciseness in model testing.
or
Detail
This PR introduces a new assertion method,
assert_error
andassert_invalid
, designed to simplify the validation testing process. With them developers can now verify the presence of specific validation errors with a single, expressive line of code. This enhancement not only improves the readability of tests but also promotes better understanding and maintenance of the validation logic within Rails models.In the same way the validation
assert_valid
andassert_no_error
ensures that the model has no errors for a specific fieldChecklist
Before submitting the PR make sure the following are checked:
[Fix #issue-number]