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
Used assertRaisesMessage() to test Django's error messages. #8257
Conversation
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.
In my experience, regular expressions introduce unneeded complexity, that's why assertRaisesRegex
isn't used. I'm not vehemently opposed to it if it makes sense in some places, but I think it can typically be avoided.
tests/null_queries/tests.py
Outdated
@@ -38,11 +38,12 @@ def test_none_as_null(self): | |||
) | |||
|
|||
# Valid query, but fails because foo isn't a keyword | |||
with self.assertRaises(FieldError): | |||
with self.assertRaisesMessage( |
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.
Please use this style in places like this:
msg = "..."
with self.assertRaisesMessage(FieldError, msg):
tests/aggregation_regress/tests.py
Outdated
@@ -430,13 +430,14 @@ def test_decimal_aggregate_annotation_filter(self): | |||
|
|||
def test_field_error(self): | |||
# Bad field requests in aggregates are caught and reported | |||
with self.assertRaises(FieldError): | |||
msg = "Cannot resolve keyword 'foo' into field. Choices are: [A-Za-z, _\-]+" | |||
with self.assertRaisesRegex(FieldError, msg): |
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.
I'd use assertRaisesMessage()
with Cannot resolve keyword 'foo' into field. Choices are:
-- there's not much value in a regex for the choices, I think.
tests/one_to_one/tests.py
Outdated
@@ -226,7 +226,8 @@ def test_related_object_cache(self): | |||
setattr(p, 'restaurant', None) | |||
|
|||
# You also can't assign an object of the wrong type here | |||
with self.assertRaises(ValueError): | |||
with self.assertRaisesMessage( | |||
ValueError, 'Cannot assign "%s": "Place.restaurant" must be a "Restaurant" instance.' % repr(p)): |
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.
Use %r
in the message.
tests/custom_managers/tests.py
Outdated
@@ -47,7 +47,8 @@ def test_queryset_copied_to_default(self): | |||
# Public methods are copied | |||
manager.public_method() | |||
# Private methods are not copied | |||
with self.assertRaises(AttributeError): | |||
msg = "'(BaseCustom)?ManagerFromCustomQuerySet' object has no attribute '_private_method'" |
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.
Can you interpolate the manager name in the message instead?
6434ed1
to
f62f8eb
Compare
tests/aggregation_regress/tests.py
Outdated
@@ -430,13 +430,17 @@ def test_decimal_aggregate_annotation_filter(self): | |||
|
|||
def test_field_error(self): | |||
# Bad field requests in aggregates are caught and reported | |||
with self.assertRaises(FieldError): | |||
msg = "Cannot resolve keyword 'foo' into field. Choices are: authors, contact, contact_id, hardbackbook, " \ |
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.
The usual style is (wrapped at 79 chars):
msg = (
"Cannot resolve "
" "
)
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.
Is 79 a hard limit? There are a few places where a single name could be fitted onto the previous line which would exceed the limit (but only by a little bit).
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.
No, it's not.
tests/aggregation_regress/tests.py
Outdated
@@ -777,7 +782,8 @@ def test_values_queryset_non_conflict(self): | |||
def test_reverse_relation_name_conflict(self): | |||
# Regression for #11256 - providing an aggregate name | |||
# that conflicts with a reverse-related name on the model raises ValueError | |||
with self.assertRaises(ValueError): | |||
with self.assertRaisesMessage( | |||
ValueError, "The annotation 'book_contact_set' conflicts with a field on the model."): |
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.
use msg="..."
style
@@ -14,7 +14,8 @@ def test_to_python(self): | |||
f = models.DecimalField(max_digits=4, decimal_places=2) | |||
self.assertEqual(f.to_python(3), Decimal('3')) | |||
self.assertEqual(f.to_python('3.14'), Decimal('3.14')) | |||
with self.assertRaises(ValidationError): | |||
msg = "%r value must be a decimal number." |
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.
No need to interpolate 'abc'
in the message -- this might be done in some existing tests for Python 2 compatibility to account for u'' prefixes on Python 2 but could be removed now.
tests/pagination/tests.py
Outdated
@@ -302,7 +302,7 @@ def test_page_getitem(self): | |||
# Make sure object_list queryset is not evaluated by an invalid __getitem__ call. | |||
# (this happens from the template engine when using eg: {% page_obj.has_previous %}) | |||
self.assertIsNone(p.object_list._result_cache) | |||
with self.assertRaises(TypeError): | |||
with self.assertRaisesMessage(TypeError, ''): |
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 is asserting '' is in the exception message which doesn't do anything useful.
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.
OK. Actually the exception message is just blank.
tests/one_to_one/tests.py
Outdated
@@ -226,7 +226,8 @@ def test_related_object_cache(self): | |||
setattr(p, 'restaurant', None) | |||
|
|||
# You also can't assign an object of the wrong type here | |||
with self.assertRaises(ValueError): | |||
msg = 'Cannot assign "%s": "Place.restaurant" must be a "Restaurant" instance.' |
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.
You could use %r
or just hard-code the repr of the class in the expected message.
tests/prefetch_related/tests.py
Outdated
list(qs) | ||
|
||
self.assertIn('prefetch_related', str(cm.exception)) | ||
self.assertIn("name", str(cm.exception)) | ||
|
||
def test_forward_m2m_to_attr_conflict(self): | ||
msg = 'to_attr=authors conflicts with a field on the Book model.' |
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.
I don't see a big advantage to this change. The coding style says to use longer lines if it makes things easier to read -- my taste is to use msg = '...'
if with self.assertRaisesMessage(ValueError, '....'):.
is much over 79 chars.
tests/raw_query/tests.py
Outdated
@@ -276,7 +276,7 @@ def test_get_item(self): | |||
first_two = Author.objects.raw(query)[0:2] | |||
self.assertEqual(len(first_two), 2) | |||
|
|||
with self.assertRaises(TypeError): | |||
with self.assertRaisesMessage(TypeError, ''): |
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.
Not useful as previously commented.
tests/settings_tests/tests.py
Outdated
@@ -174,28 +175,30 @@ def test_override_change(self): | |||
del settings.TEST | |||
|
|||
def test_override_doesnt_leak(self): | |||
with self.assertRaises(AttributeError): | |||
msg = "'Settings' object has no attribute %r" % 'TEST' |
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.
I guess you meant to use self.msg
here?
tests/settings_tests/tests.py
Outdated
@@ -148,6 +148,7 @@ class SettingsTests(SimpleTestCase): | |||
def setUp(self): | |||
self.testvalue = None | |||
signals.setting_changed.connect(self.signal_callback) | |||
self.msg = "'Settings' object has no attribute %r" |
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 can be a class-level attribute -- no need to assign it in setUp
for each test. Probably should have a more descriptive name like no_attribute_msg
.
tests/timezones/tests.py
Outdated
@@ -123,7 +123,8 @@ def test_aware_datetime_in_other_timezone(self): | |||
@skipIfDBFeature('supports_timezones') | |||
def test_aware_datetime_unsupported(self): | |||
dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) | |||
with self.assertRaises(ValueError): | |||
msg = r'%s backend does not support timezone-aware datetimes when USE_TZ is False.' % connection.vendor |
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.
I'd use assertRaisesMessage()
with msg = 'backend does not support timezone-aware datetimes when USE_TZ is False.'
. There's a small chance that messages from third-party backends might not match the connection.vendor
convention and I don't think it's a critical part of the assertion.
8b1d9bb
to
b8cd00d
Compare
I have still some places to change; it's not going to be exhaustive (there are more than 1000, and in some places |
46593df
to
74e6405
Compare
@@ -18,7 +18,8 @@ def test_uuidfield_2(self): | |||
|
|||
def test_uuidfield_3(self): | |||
field = UUIDField() | |||
with self.assertRaises(ValidationError) as cm: | |||
msg = 'Enter a valid UUID.' | |||
with self.assertRaisesMessage(ValidationError, msg) as cm: | |||
field.clean('550e8400') | |||
self.assertEqual(cm.exception.messages[0], 'Enter a valid UUID.') |
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.
I suppose this line could be deleted because of assertRaisesMessage
?
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.
I forget if there was a reason to prefer the current version but yes, we don't need the check twice.
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.
There are more instances of this pattern if you grep
for cm.exception.messages
. However, in some of them, e.g., in tests/auth_tests/test_validators.py
), more checks are done are done on the exception (args
, code
) and it's a bit different for the forms.
Hello, I disagree with the general gist of this PR and posted about it to django-developers. |
7294d6e
to
6e84568
Compare
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 is enough for one commit, more can be done later if needed. Please add 0846e40.
tests/update_only_fields/tests.py
Outdated
s.save(update_fields=['first_name']) | ||
|
||
with self.assertRaises(ValueError): | ||
with self.assertRaisesMessage(ValueError, msg % ''): |
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.
'' -> 'name'?
tests/aggregation_regress/tests.py
Outdated
@@ -738,19 +748,22 @@ def test_more_more(self): | |||
|
|||
def test_duplicate_alias(self): | |||
# Regression for #11256 - duplicating a default alias raises ValueError. | |||
with self.assertRaises(ValueError): | |||
msg = "The named annotation 'authors__age__avg' conflicts with the default name for another annotation." |
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.
multiline please
tests/basic/tests.py
Outdated
@@ -397,7 +397,7 @@ def test_eq(self): | |||
def test_hash(self): | |||
# Value based on PK | |||
self.assertEqual(hash(Article(id=1)), hash(1)) | |||
with self.assertRaises(TypeError): | |||
with self.assertRaisesMessage(TypeError, 'Model instances without primary key value are unhashable'): |
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.
use a msg variable
tests/custom_managers/tests.py
Outdated
@@ -90,7 +92,7 @@ def test_manager_attributes(self): | |||
querysets. | |||
""" | |||
Person.custom_queryset_custom_manager.manager_only() | |||
with self.assertRaises(AttributeError): | |||
with self.assertRaisesMessage(AttributeError, "'CustomQuerySet' object has no attribute 'manager_only'"): |
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.
use a msg variable
tests/expressions/tests.py
Outdated
@@ -281,7 +282,8 @@ def test_new_object_create(self): | |||
|
|||
def test_object_create_with_aggregate(self): | |||
# Aggregates are not allowed when inserting new data | |||
with self.assertRaisesMessage(FieldError, 'Aggregate functions are not allowed in this query'): | |||
msg = 'Aggregate functions are not allowed in this query' |
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.
Don't need to change this.
tests/migrations/test_commands.py
Outdated
call_command("makemigrations") | ||
exception_message = str(context.exception) | ||
self.assertIn(( |
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.
self.assertIn(
'Conflicting migrations detected; multiple leaf nodes '
'in the migration graph:',
exception_message
)
tests/multiple_database/tests.py
Outdated
@@ -321,27 +321,30 @@ def test_m2m_cross_database_protection(self): | |||
|
|||
mark = Person.objects.using('other').create(name="Mark Pilgrim") | |||
# Set a foreign key set with an object from a different database | |||
with self.assertRaises(ValueError): | |||
msg = 'Cannot assign "<Person: Marty Alchin>": the current database router prevents this relation.' |
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.
multiline messages much longer than 79 chars please
tests/sitemaps_tests/test_http.py
Outdated
@@ -17,6 +17,11 @@ | |||
|
|||
class HTTPSitemapTests(SitemapTestsBase): | |||
|
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.
no blank line before class attrs needed
@@ -6,6 +6,7 @@ | |||
|
|||
|
|||
class WithTagTests(SimpleTestCase): | |||
atleast_with_one_msg = "'with' expected at least one variable assignment" |
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.
at_least
tests/update_only_fields/tests.py
Outdated
@@ -120,7 +120,8 @@ def test_update_fields_m2m(self): | |||
a2 = Account.objects.create(num=2) | |||
e1.accounts.set([a1, a2]) | |||
|
|||
with self.assertRaises(ValueError): | |||
msg = 'The following fields do not exist in this model or are m2m fields: accounts' |
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.
Could go on the class-level to avoid duplicating it in two tests.
3f3f52e
to
b268248
Compare
As requested in #8251 a separate PR to replace
self.assertRaises
withself.assertRaisesMessage
.self.assertRaisesRegex
is not used anywhere in the testsuite, so instead of replacing everything (which takes time), I've introduced it in a few places.IntegrityError
,DatabaseError
,DoesNotExist
will be ignored.