diff --git a/tests/admin_registration/tests.py b/tests/admin_registration/tests.py index 125d8eae61..8601328647 100644 --- a/tests/admin_registration/tests.py +++ b/tests/admin_registration/tests.py @@ -30,7 +30,8 @@ class TestRegistration(SimpleTestCase): def test_prevent_double_registration(self): self.site.register(Person) - with self.assertRaises(admin.sites.AlreadyRegistered): + msg = 'The model Person is already registered' + with self.assertRaisesMessage(admin.sites.AlreadyRegistered, msg): self.site.register(Person) def test_registration_with_star_star_options(self): @@ -55,7 +56,8 @@ class TestRegistration(SimpleTestCase): Exception is raised when trying to register an abstract model. Refs #12004. """ - with self.assertRaises(ImproperlyConfigured): + msg = 'The model Location is abstract, so it cannot be registered with admin.' + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.site.register(Location) def test_is_registered_model(self): diff --git a/tests/admin_utils/tests.py b/tests/admin_utils/tests.py index 09136ea72a..1f475c2f5b 100644 --- a/tests/admin_utils/tests.py +++ b/tests/admin_utils/tests.py @@ -222,7 +222,7 @@ class UtilsTests(SimpleTestCase): "article" ) - with self.assertRaises(AttributeError): + with self.assertRaisesMessage(AttributeError, "Unable to lookup 'unknown' on Article"): label_for_field("unknown", Article) def test_callable(obj): diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py index 7dc9a95688..91e993a6d1 100644 --- a/tests/aggregation_regress/tests.py +++ b/tests/aggregation_regress/tests.py @@ -431,13 +431,23 @@ class AggregationTests(TestCase): 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, id, isbn, name, pages, price, " + "pubdate, publisher, publisher_id, rating, store, tags" + ) + with self.assertRaisesMessage(FieldError, msg): Book.objects.all().aggregate(num_authors=Count('foo')) - with self.assertRaises(FieldError): + with self.assertRaisesMessage(FieldError, msg): Book.objects.all().annotate(num_authors=Count('foo')) - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'foo' into field. Choices are: authors, " + "contact, contact_id, hardbackbook, id, isbn, name, num_authors, " + "pages, price, pubdate, publisher, publisher_id, rating, store, tags" + ) + with self.assertRaisesMessage(FieldError, msg): Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo')) def test_more(self): @@ -738,19 +748,25 @@ class AggregationTests(TestCase): 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." + ) + with self.assertRaisesMessage(ValueError, msg): Book.objects.all().annotate(Avg('authors__age'), authors__age__avg=Avg('authors__age')) def test_field_name_conflict(self): # Regression for #11256 - providing an aggregate name # that conflicts with a field name on the model raises ValueError - with self.assertRaises(ValueError): + msg = "The annotation 'age' conflicts with a field on the model." + with self.assertRaisesMessage(ValueError, msg): Author.objects.annotate(age=Avg('friends__age')) def test_m2m_name_conflict(self): # Regression for #11256 - providing an aggregate name # that conflicts with an m2m name on the model raises ValueError - with self.assertRaises(ValueError): + msg = "The annotation 'friends' conflicts with a field on the model." + with self.assertRaisesMessage(ValueError, msg): Author.objects.annotate(friends=Count('friends')) def test_values_queryset_non_conflict(self): @@ -778,7 +794,8 @@ class AggregationTests(TestCase): 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): + msg = "The annotation 'book_contact_set' conflicts with a field on the model." + with self.assertRaisesMessage(ValueError, msg): Author.objects.annotate(book_contact_set=Avg('friends__age')) def test_pickle(self): @@ -937,7 +954,8 @@ class AggregationTests(TestCase): # Regression for #10766 - Shouldn't be able to reference an aggregate # fields in an aggregate() call. - with self.assertRaises(FieldError): + msg = "Cannot compute Avg('mean_age'): 'mean_age' is an aggregate" + with self.assertRaisesMessage(FieldError, msg): Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age')) def test_empty_filter_count(self): diff --git a/tests/apps/tests.py b/tests/apps/tests.py index 50b3927434..2910220b94 100644 --- a/tests/apps/tests.py +++ b/tests/apps/tests.py @@ -53,7 +53,8 @@ class AppsTests(SimpleTestCase): """ Tests when INSTALLED_APPS contains an incorrect app config. """ - with self.assertRaises(ImproperlyConfigured): + msg = "'apps.apps.BadConfig' must supply a name attribute." + with self.assertRaisesMessage(ImproperlyConfigured, msg): with self.settings(INSTALLED_APPS=['apps.apps.BadConfig']): pass @@ -61,7 +62,8 @@ class AppsTests(SimpleTestCase): """ Tests when INSTALLED_APPS contains a class that isn't an app config. """ - with self.assertRaises(ImproperlyConfigured): + msg = "'apps.apps.NotAConfig' isn't a subclass of AppConfig." + with self.assertRaisesMessage(ImproperlyConfigured, msg): with self.settings(INSTALLED_APPS=['apps.apps.NotAConfig']): pass diff --git a/tests/auth_tests/test_auth_backends.py b/tests/auth_tests/test_auth_backends.py index ea9ddd426c..744f8ad817 100644 --- a/tests/auth_tests/test_auth_backends.py +++ b/tests/auth_tests/test_auth_backends.py @@ -445,7 +445,11 @@ class NoBackendsTest(TestCase): self.user = User.objects.create_user('test', 'test@example.com', 'test') def test_raises_exception(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'No authentication backends have been defined. ' + 'Does AUTHENTICATION_BACKENDS contain anything?' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.user.has_perm(('perm', TestObj())) @@ -626,7 +630,11 @@ class ImproperlyConfiguredUserModelTest(TestCase): request = HttpRequest() request.session = self.client.session - with self.assertRaises(ImproperlyConfigured): + msg = ( + "AUTH_USER_MODEL refers to model 'thismodel.doesntexist' " + "that has not been installed" + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): get_user(request) diff --git a/tests/auth_tests/test_basic.py b/tests/auth_tests/test_basic.py index b2c70faffb..80a56f8ca3 100644 --- a/tests/auth_tests/test_basic.py +++ b/tests/auth_tests/test_basic.py @@ -97,13 +97,18 @@ class BasicTestCase(TestCase): @override_settings(AUTH_USER_MODEL='badsetting') def test_swappable_user_bad_setting(self): "The alternate user setting must point to something in the format app.model" - with self.assertRaises(ImproperlyConfigured): + msg = "AUTH_USER_MODEL must be of the form 'app_label.model_name'" + with self.assertRaisesMessage(ImproperlyConfigured, msg): get_user_model() @override_settings(AUTH_USER_MODEL='thismodel.doesntexist') def test_swappable_user_nonexistent_model(self): "The current user model must point to an installed model" - with self.assertRaises(ImproperlyConfigured): + msg = ( + "AUTH_USER_MODEL refers to model 'thismodel.doesntexist' " + "that has not been installed" + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): get_user_model() def test_user_verbose_names_translatable(self): diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py index c4368b3bcf..c43091d932 100644 --- a/tests/auth_tests/test_management.py +++ b/tests/auth_tests/test_management.py @@ -158,7 +158,8 @@ class ChangepasswordManagementCommandTestCase(TestCase): A CommandError should be thrown by handle() if the user enters in mismatched passwords three times. """ - with self.assertRaises(CommandError): + msg = "Aborting password change for user 'joe' after 3 attempts" + with self.assertRaisesMessage(CommandError, msg): call_command('changepassword', username='joe', stdout=self.stdout, stderr=self.stderr) @mock.patch.object(changepassword.Command, '_get_pass', return_value='1234567890') @@ -316,7 +317,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase): # We skip validation because the temporary substitution of the # swappable User model messes with validation. new_io = StringIO() - with self.assertRaises(CommandError): + with self.assertRaisesMessage(CommandError, 'You must use --email with --noinput.'): call_command( "createsuperuser", interactive=False, diff --git a/tests/basic/tests.py b/tests/basic/tests.py index d55277c0f2..c44e17dd24 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -335,8 +335,8 @@ class ModelTest(TestCase): self.assertEqual(article.headline, notlazy) def test_emptyqs(self): - # Can't be instantiated - with self.assertRaises(TypeError): + msg = "EmptyQuerySet can't be instantiated" + with self.assertRaisesMessage(TypeError, msg): EmptyQuerySet() self.assertIsInstance(Article.objects.none(), EmptyQuerySet) self.assertNotIsInstance('', EmptyQuerySet) @@ -397,7 +397,8 @@ class ModelTest(TestCase): def test_hash(self): # Value based on PK self.assertEqual(hash(Article(id=1)), hash(1)) - with self.assertRaises(TypeError): + msg = 'Model instances without primary key value are unhashable' + with self.assertRaisesMessage(TypeError, msg): # No PK value -> unhashable (because save() would then change # hash) hash(Article()) @@ -618,7 +619,7 @@ class SelectOnSaveTests(TestCase): with self.assertNumQueries(1): asos.save(force_update=True) Article.objects.all().delete() - with self.assertRaises(DatabaseError): + with self.assertRaisesMessage(DatabaseError, 'Forced update did not affect any rows.'): with self.assertNumQueries(1): asos.save(force_update=True) @@ -653,9 +654,13 @@ class SelectOnSaveTests(TestCase): # This is not wanted behavior, but this is how Django has always # behaved for databases that do not return correct information # about matched rows for UPDATE. - with self.assertRaises(DatabaseError): + with self.assertRaisesMessage(DatabaseError, 'Forced update did not affect any rows.'): asos.save(force_update=True) - with self.assertRaises(DatabaseError): + msg = ( + "An error occurred in the current transaction. You can't " + "execute queries until the end of the 'atomic' block." + ) + with self.assertRaisesMessage(DatabaseError, msg): asos.save(update_fields=['pub_date']) finally: Article._base_manager._queryset_class = orig_class @@ -688,7 +693,8 @@ class ModelRefreshTests(TestCase): def test_unknown_kwarg(self): s = SelfRef.objects.create() - with self.assertRaises(TypeError): + msg = "refresh_from_db() got an unexpected keyword argument 'unknown_kwarg'" + with self.assertRaisesMessage(TypeError, msg): s.refresh_from_db(unknown_kwarg=10) def test_refresh_fk(self): diff --git a/tests/bulk_create/tests.py b/tests/bulk_create/tests.py index 6b3e0bedc4..2439050623 100644 --- a/tests/bulk_create/tests.py +++ b/tests/bulk_create/tests.py @@ -108,7 +108,8 @@ class BulkCreateTests(TestCase): """ valid_country = Country(name='Germany', iso_two_letter='DE') invalid_country = Country(id=0, name='Poland', iso_two_letter='PL') - with self.assertRaises(ValueError): + msg = 'The database backend does not accept 0 as a value for AutoField.' + with self.assertRaisesMessage(ValueError, msg): Country.objects.bulk_create([valid_country, invalid_country]) def test_batch_same_vals(self): diff --git a/tests/check_framework/tests.py b/tests/check_framework/tests.py index 2128da3dc6..abb4298c65 100644 --- a/tests/check_framework/tests.py +++ b/tests/check_framework/tests.py @@ -179,7 +179,8 @@ class CheckCommandTests(SimpleTestCase): @override_system_checks([simple_system_check, tagged_system_check]) def test_invalid_tag(self): - with self.assertRaises(CommandError): + msg = 'There is no system check with the "missingtag" tag.' + with self.assertRaisesMessage(CommandError, msg): call_command('check', tags=['missingtag']) @override_system_checks([simple_system_check]) diff --git a/tests/custom_columns/tests.py b/tests/custom_columns/tests.py index 2d7044b8de..f5dfd9c0cd 100644 --- a/tests/custom_columns/tests.py +++ b/tests/custom_columns/tests.py @@ -37,7 +37,11 @@ class CustomColumnsTests(TestCase): ) def test_field_error(self): - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'firstname' into field. Choices are: " + "Author_ID, article, first_name, last_name, primary_set" + ) + with self.assertRaisesMessage(FieldError, msg): Author.objects.filter(firstname__exact="John") def test_attribute_error(self): diff --git a/tests/custom_lookups/tests.py b/tests/custom_lookups/tests.py index 4a477b4687..d39ebe6cdc 100644 --- a/tests/custom_lookups/tests.py +++ b/tests/custom_lookups/tests.py @@ -319,7 +319,8 @@ class BilateralTransformTests(TestCase): def test_bilateral_inner_qs(self): with register_lookup(models.CharField, UpperBilateralTransform): - with self.assertRaises(NotImplementedError): + msg = 'Bilateral transformations on nested querysets are not supported.' + with self.assertRaisesMessage(NotImplementedError, msg): Author.objects.filter(name__upper__in=Author.objects.values_list('name')) def test_bilateral_multi_value(self): @@ -501,13 +502,14 @@ class LookupTransformCallOrderTests(TestCase): def test_call_order(self): with register_lookup(models.DateField, TrackCallsYearTransform): # junk lookup - tries lookup, then transform, then fails - with self.assertRaises(FieldError): + msg = "Unsupported lookup 'junk' for IntegerField or join on the field not permitted." + with self.assertRaisesMessage(FieldError, msg): Author.objects.filter(birthdate__testyear__junk=2012) self.assertEqual(TrackCallsYearTransform.call_order, ['lookup', 'transform']) TrackCallsYearTransform.call_order = [] # junk transform - tries transform only, then fails - with self.assertRaises(FieldError): + with self.assertRaisesMessage(FieldError, msg): Author.objects.filter(birthdate__testyear__junk__more_junk=2012) self.assertEqual(TrackCallsYearTransform.call_order, ['transform']) diff --git a/tests/custom_managers/tests.py b/tests/custom_managers/tests.py index d095036f10..ee2ac1d552 100644 --- a/tests/custom_managers/tests.py +++ b/tests/custom_managers/tests.py @@ -58,7 +58,8 @@ class CustomManagerTests(TestCase): # Methods with queryset_only=False are copied even if they are private. manager._optin_private_method() # Methods with queryset_only=True aren't copied even if they are public. - with self.assertRaises(AttributeError): + msg = "%r object has no attribute 'optout_public_method'" % manager.__class__.__name__ + with self.assertRaisesMessage(AttributeError, msg): manager.optout_public_method() def test_manager_use_queryset_methods(self): @@ -93,7 +94,8 @@ class CustomManagerTests(TestCase): querysets. """ Person.custom_queryset_custom_manager.manager_only() - with self.assertRaises(AttributeError): + msg = "'CustomQuerySet' object has no attribute 'manager_only'" + with self.assertRaisesMessage(AttributeError, msg): Person.custom_queryset_custom_manager.all().manager_only() def test_queryset_and_manager(self): @@ -116,7 +118,8 @@ class CustomManagerTests(TestCase): The default manager, "objects", doesn't exist, because a custom one was provided. """ - with self.assertRaises(AttributeError): + msg = "type object 'Book' has no attribute 'objects'" + with self.assertRaisesMessage(AttributeError, msg): Book.objects def test_filtering(self): diff --git a/tests/custom_pk/tests.py b/tests/custom_pk/tests.py index 7c89b6d120..da0cff14cc 100644 --- a/tests/custom_pk/tests.py +++ b/tests/custom_pk/tests.py @@ -145,7 +145,7 @@ class BasicCustomPKTests(TestCase): # Or we can use the real attribute name for the primary key: self.assertEqual(e.employee_code, 123) - with self.assertRaises(AttributeError): + with self.assertRaisesMessage(AttributeError, "'Employee' object has no attribute 'id'"): e.id def test_in_bulk(self): diff --git a/tests/delete/tests.py b/tests/delete/tests.py index 0c7e218d38..98467efb6a 100644 --- a/tests/delete/tests.py +++ b/tests/delete/tests.py @@ -60,7 +60,11 @@ class OnDeleteTests(TestCase): def test_protect(self): a = create_a('protect') - with self.assertRaises(IntegrityError): + msg = ( + "Cannot delete some instances of model 'R' because they are " + "referenced through a protected foreign key: 'A.protect'" + ) + with self.assertRaisesMessage(IntegrityError, msg): a.protect.delete() def test_do_nothing(self): diff --git a/tests/delete_regress/tests.py b/tests/delete_regress/tests.py index 7472731e94..7f913257ab 100644 --- a/tests/delete_regress/tests.py +++ b/tests/delete_regress/tests.py @@ -244,9 +244,10 @@ class ProxyDeleteTest(TestCase): self.assertEqual(len(FooFileProxy.objects.all()), 0) def test_19187_values(self): - with self.assertRaises(TypeError): + msg = 'Cannot call delete() after .values() or .values_list()' + with self.assertRaisesMessage(TypeError, msg): Image.objects.values().delete() - with self.assertRaises(TypeError): + with self.assertRaisesMessage(TypeError, msg): Image.objects.values_list().delete() diff --git a/tests/distinct_on_fields/tests.py b/tests/distinct_on_fields/tests.py index 6bb518d2b1..93a332cf83 100644 --- a/tests/distinct_on_fields/tests.py +++ b/tests/distinct_on_fields/tests.py @@ -97,16 +97,18 @@ class DistinctOnTests(TestCase): def test_distinct_not_implemented_checks(self): # distinct + annotate not allowed - with self.assertRaises(NotImplementedError): + msg = 'annotate() + distinct(fields) is not implemented.' + with self.assertRaisesMessage(NotImplementedError, msg): Celebrity.objects.annotate(Max('id')).distinct('id')[0] - with self.assertRaises(NotImplementedError): + with self.assertRaisesMessage(NotImplementedError, msg): Celebrity.objects.distinct('id').annotate(Max('id'))[0] # However this check is done only when the query executes, so you # can use distinct() to remove the fields before execution. Celebrity.objects.distinct('id').annotate(Max('id')).distinct()[0] # distinct + aggregate not allowed - with self.assertRaises(NotImplementedError): + msg = 'aggregate() + distinct(fields) not implemented.' + with self.assertRaisesMessage(NotImplementedError, msg): Celebrity.objects.distinct('id').aggregate(Max('id')) def test_distinct_on_in_ordered_subquery(self): diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index aa090d2746..8952045002 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -246,7 +246,8 @@ class BasicExpressionsTests(TestCase): ) with transaction.atomic(): - with self.assertRaises(FieldError): + msg = "Joined field references are not permitted in this query" + with self.assertRaisesMessage(FieldError, msg): Company.objects.exclude( ceo__firstname=F('point_of_contact__firstname') ).update(name=F('point_of_contact__lastname')) @@ -293,13 +294,15 @@ class BasicExpressionsTests(TestCase): def test(): test_gmbh.point_of_contact = F("ceo") - with self.assertRaises(ValueError): + msg = 'F(ceo)": "Company.point_of_contact" must be a "Employee" instance.' + with self.assertRaisesMessage(ValueError, msg): test() test_gmbh.point_of_contact = test_gmbh.ceo test_gmbh.save() test_gmbh.name = F("ceo__last_name") - with self.assertRaises(FieldError): + msg = 'Joined field references are not permitted in this query' + with self.assertRaisesMessage(FieldError, msg): test_gmbh.save() def test_object_update_unsaved_objects(self): diff --git a/tests/flatpages_tests/test_templatetags.py b/tests/flatpages_tests/test_templatetags.py index 688d85a224..1ce8f65079 100644 --- a/tests/flatpages_tests/test_templatetags.py +++ b/tests/flatpages_tests/test_templatetags.py @@ -128,17 +128,21 @@ class FlatpageTemplateTagTests(TestCase): def render(t): return Template(t).render(Context()) - with self.assertRaises(TemplateSyntaxError): + msg = ( + "get_flatpages expects a syntax of get_flatpages " + "['url_starts_with'] [for user] as context_name" + ) + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages %}") - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages as %}") - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages cheesecake flatpages %}") - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages as flatpages asdf %}") - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages cheesecake user as flatpages %}") - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages for user as flatpages asdf %}") - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): render("{% load flatpages %}{% get_flatpages prefix for user as flatpages asdf %}") diff --git a/tests/force_insert_update/tests.py b/tests/force_insert_update/tests.py index 2232283cb8..8ffe98ad4f 100644 --- a/tests/force_insert_update/tests.py +++ b/tests/force_insert_update/tests.py @@ -20,13 +20,15 @@ class ForceTests(TestCase): # Won't work because force_update and force_insert are mutually # exclusive c.value = 4 - with self.assertRaises(ValueError): + msg = 'Cannot force both insert and updating in model saving.' + with self.assertRaisesMessage(ValueError, msg): c.save(force_insert=True, force_update=True) # Try to update something that doesn't have a primary key in the first # place. c1 = Counter(name="two", value=2) - with self.assertRaises(ValueError): + msg = 'Cannot force an update in save() with no primary key.' + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(): c1.save(force_update=True) c1.save(force_insert=True) @@ -40,7 +42,8 @@ class ForceTests(TestCase): # Trying to update should still fail, even with manual primary keys, if # the data isn't in the database already. obj = WithCustomPK(name=1, value=1) - with self.assertRaises(DatabaseError): + msg = 'Forced update did not affect any rows.' + with self.assertRaisesMessage(DatabaseError, msg): with transaction.atomic(): obj.save(force_update=True) diff --git a/tests/foreign_object/tests.py b/tests/foreign_object/tests.py index e74732ab72..59d4357802 100644 --- a/tests/foreign_object/tests.py +++ b/tests/foreign_object/tests.py @@ -368,7 +368,12 @@ class MultiColumnFKTests(TestCase): ArticleTag.objects.create(article=a1, name="foo") self.assertEqual(Article.objects.filter(tag__name="foo").count(), 1) self.assertEqual(Article.objects.filter(tag__name="bar").count(), 0) - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'tags' into field. Choices are: " + "active_translation, active_translation_q, articletranslation, " + "id, idea_things, newsarticle, pub_date, tag" + ) + with self.assertRaisesMessage(FieldError, msg): Article.objects.filter(tags__name="foo") def test_many_to_many_related_query_name(self): @@ -377,7 +382,12 @@ class MultiColumnFKTests(TestCase): a1.ideas.add(i1) self.assertEqual(Article.objects.filter(idea_things__name="idea1").count(), 1) self.assertEqual(Article.objects.filter(idea_things__name="idea2").count(), 0) - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'ideas' into field. Choices are: " + "active_translation, active_translation_q, articletranslation, " + "id, idea_things, newsarticle, pub_date, tag" + ) + with self.assertRaisesMessage(FieldError, msg): Article.objects.filter(ideas__name="idea1") @translation.override('fi') diff --git a/tests/forms_tests/field_tests/test_uuidfield.py b/tests/forms_tests/field_tests/test_uuidfield.py index 08498ab9c9..ed08efd659 100644 --- a/tests/forms_tests/field_tests/test_uuidfield.py +++ b/tests/forms_tests/field_tests/test_uuidfield.py @@ -18,9 +18,8 @@ class UUIDFieldTest(SimpleTestCase): def test_uuidfield_3(self): field = UUIDField() - with self.assertRaises(ValidationError) as cm: + with self.assertRaisesMessage(ValidationError, 'Enter a valid UUID.'): field.clean('550e8400') - self.assertEqual(cm.exception.messages[0], 'Enter a valid UUID.') def test_uuidfield_4(self): field = UUIDField() diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py index 29f138b2d1..defa46b730 100644 --- a/tests/forms_tests/tests/test_formsets.py +++ b/tests/forms_tests/tests/test_formsets.py @@ -1338,7 +1338,8 @@ ArticleFormSet = formset_factory(ArticleForm) class TestIsBoundBehavior(SimpleTestCase): def test_no_data_raises_validation_error(self): - with self.assertRaises(ValidationError): + msg = 'ManagementForm data is missing or has been tampered with' + with self.assertRaisesMessage(ValidationError, msg): ArticleFormSet({}).is_valid() def test_with_management_data_attrs_work_fine(self): diff --git a/tests/forms_tests/tests/tests.py b/tests/forms_tests/tests/tests.py index 043b8e0edc..a54ad09c26 100644 --- a/tests/forms_tests/tests/tests.py +++ b/tests/forms_tests/tests/tests.py @@ -247,7 +247,11 @@ class RelatedModelFormTests(SimpleTestCase): model = A fields = '__all__' - with self.assertRaises(ValueError): + msg = ( + "Cannot create form field for 'ref' yet, because " + "its related model 'B' has not been loaded yet" + ) + with self.assertRaisesMessage(ValueError, msg): ModelFormMetaclass('Form', (ModelForm,), {'Meta': Meta}) class B(models.Model): diff --git a/tests/generic_relations/tests.py b/tests/generic_relations/tests.py index 91bb616420..d3aa3b15f8 100644 --- a/tests/generic_relations/tests.py +++ b/tests/generic_relations/tests.py @@ -347,7 +347,12 @@ class GenericRelationsTests(TestCase): def test_generic_relation_related_name_default(self): # GenericRelation isn't usable from the reverse side by default. - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'vegetable' into field. Choices are: " + "animal, content_object, content_type, content_type_id, id, " + "manualpk, object_id, tag, valuabletaggeditem" + ) + with self.assertRaisesMessage(FieldError, msg): TaggedItem.objects.filter(vegetable__isnull=True) def test_multiple_gfk(self): diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py index 8215e67e1a..1579408712 100644 --- a/tests/generic_views/test_base.py +++ b/tests/generic_views/test_base.py @@ -273,7 +273,11 @@ class TemplateViewTest(SimpleTestCase): """ A template view must provide a template name. """ - with self.assertRaises(ImproperlyConfigured): + msg = ( + "TemplateResponseMixin requires either a definition of " + "'template_name' or an implementation of 'get_template_names()'" + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.get('/template/no_template/') @require_jinja2 diff --git a/tests/generic_views/test_dates.py b/tests/generic_views/test_dates.py index 454b9e5a1d..ff3b09ddf3 100644 --- a/tests/generic_views/test_dates.py +++ b/tests/generic_views/test_dates.py @@ -79,7 +79,11 @@ class ArchiveIndexViewTests(TestDataMixin, TestCase): self.assertTemplateUsed(res, 'generic_views/book_detail.html') def test_archive_view_invalid(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'BookArchive is missing a QuerySet. Define BookArchive.model, ' + 'BookArchive.queryset, or override BookArchive.get_queryset().' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.get('/dates/books/invalid/') def test_archive_view_by_month(self): diff --git a/tests/generic_views/test_detail.py b/tests/generic_views/test_detail.py index da20db066e..002435dfce 100644 --- a/tests/generic_views/test_detail.py +++ b/tests/generic_views/test_detail.py @@ -175,7 +175,11 @@ class DetailViewTest(TestCase): self.client.get('/detail/author/invalid/url/') def test_invalid_queryset(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'AuthorDetail is missing a QuerySet. Define AuthorDetail.model, ' + 'AuthorDetail.queryset, or override AuthorDetail.get_queryset().' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.get('/detail/author/invalid/qs/') def test_non_model_object_with_meta(self): diff --git a/tests/generic_views/test_edit.py b/tests/generic_views/test_edit.py index 3a68fc4098..522189a475 100644 --- a/tests/generic_views/test_edit.py +++ b/tests/generic_views/test_edit.py @@ -159,7 +159,11 @@ class CreateViewTests(TestCase): self.assertQuerysetEqual(Author.objects.all(), ['']) def test_create_without_redirect(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'No URL to redirect to. Either provide a url or define a ' + 'get_absolute_url method on the Model.' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.post('/edit/authors/create/naive/', {'name': 'Randall Munroe', 'slug': 'randall-munroe'}) def test_create_restricted(self): @@ -312,9 +316,11 @@ class UpdateViewTests(TestCase): name='Randall Munroe', slug='randall-munroe', ) - # Should raise exception -- No redirect URL provided, and no - # get_absolute_url provided - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'No URL to redirect to. Either provide a url or define a ' + 'get_absolute_url method on the Model.' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.post( '/edit/author/%d/update/naive/' % a.pk, {'name': 'Randall Munroe (author of xkcd)', 'slug': 'randall-munroe'} @@ -404,7 +410,6 @@ class DeleteViewTests(TestCase): name='Randall Munroe', slug='randall-munroe', ) - # Should raise exception -- No redirect URL provided, and no - # get_absolute_url provided - with self.assertRaises(ImproperlyConfigured): + msg = 'No URL to redirect to. Provide a success_url.' + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.post('/edit/author/%d/delete/naive/' % a.pk) diff --git a/tests/generic_views/test_list.py b/tests/generic_views/test_list.py index 429d46f50c..ea57b6af9a 100644 --- a/tests/generic_views/test_list.py +++ b/tests/generic_views/test_list.py @@ -200,7 +200,11 @@ class ListViewTests(TestCase): self.assertTemplateUsed(res, 'generic_views/author_list.html') def test_missing_items(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'AuthorList is missing a QuerySet. Define AuthorList.model, ' + 'AuthorList.queryset, or override AuthorList.get_queryset().' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.get('/list/authors/invalid/') def test_paginated_list_view_does_not_load_entire_table(self): diff --git a/tests/get_earliest_or_latest/tests.py b/tests/get_earliest_or_latest/tests.py index 2322e0a91c..c59f4324f0 100644 --- a/tests/get_earliest_or_latest/tests.py +++ b/tests/get_earliest_or_latest/tests.py @@ -118,7 +118,11 @@ class EarliestOrLatestTests(TestCase): # "get_latest_by" set -- just pass in the field name manually. Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1)) p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3)) - with self.assertRaises(AssertionError): + msg = ( + "earliest() and latest() require either a field_name parameter or " + "'get_latest_by' in the model" + ) + with self.assertRaisesMessage(AssertionError, msg): Person.objects.latest() self.assertEqual(Person.objects.latest("birthday"), p2) diff --git a/tests/i18n/patterns/tests.py b/tests/i18n/patterns/tests.py index a1d4269735..0f69f0d8c5 100644 --- a/tests/i18n/patterns/tests.py +++ b/tests/i18n/patterns/tests.py @@ -78,7 +78,8 @@ class URLPrefixTests(URLTestCaseBase): @override_settings(ROOT_URLCONF='i18n.patterns.urls.wrong') def test_invalid_prefix_use(self): - with self.assertRaises(ImproperlyConfigured): + msg = 'Using i18n_patterns in an included URLconf is not allowed.' + with self.assertRaisesMessage(ImproperlyConfigured, msg): reverse('account:register') diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index 289d8dde1b..41270cd19f 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -315,7 +315,11 @@ class LookupTests(TestCase): # However, an exception FieldDoesNotExist will be thrown if you specify # a nonexistent field name in values() (a field that is neither in the # model nor in extra(select)). - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'id_plus_two' into field. Choices are: " + "author, author_id, headline, id, id_plus_one, pub_date, slug, tag" + ) + with self.assertRaisesMessage(FieldError, msg): Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_two') # If you don't specify field names to values(), all are returned. self.assertSequenceEqual( @@ -733,11 +737,16 @@ class LookupTests(TestCase): """ A lookup query containing non-fields raises the proper exception. """ - with self.assertRaises(FieldError): + msg = "Unsupported lookup 'blahblah' for CharField or join on the field not permitted." + with self.assertRaisesMessage(FieldError, msg): Article.objects.filter(headline__blahblah=99) - with self.assertRaises(FieldError): + with self.assertRaisesMessage(FieldError, msg): Article.objects.filter(headline__blahblah__exact=99) - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'blahblah' into field. Choices are: " + "author, author_id, headline, id, pub_date, slug, tag" + ) + with self.assertRaisesMessage(FieldError, msg): Article.objects.filter(blahblah=99) def test_lookup_collision(self): diff --git a/tests/m2m_regress/tests.py b/tests/m2m_regress/tests.py index 6d4a4f02ed..b38ccf83be 100644 --- a/tests/m2m_regress/tests.py +++ b/tests/m2m_regress/tests.py @@ -102,7 +102,7 @@ class M2MRegressionTests(TestCase): c1 = TagCollection.objects.create(name='c1') c1.tags.set([t1, t2]) - with self.assertRaises(TypeError): + with self.assertRaisesMessage(TypeError, "'int' object is not iterable"): c1.tags.set(7) c1.refresh_from_db() diff --git a/tests/many_to_many/tests.py b/tests/many_to_many/tests.py index d81d88b176..5b1c6e1516 100644 --- a/tests/many_to_many/tests.py +++ b/tests/many_to_many/tests.py @@ -29,7 +29,11 @@ class ManyToManyTests(TestCase): # Create an Article. a5 = Article(headline='Django lets you reate Web apps easily') # You can't associate it with a Publication until it's been saved. - with self.assertRaises(ValueError): + msg = ( + '"" needs to have ' + 'a value for field "id" before this many-to-many relationship can be used.' + ) + with self.assertRaisesMessage(ValueError, msg): getattr(a5, 'publications') # Save it! a5.save() diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py index 0382a9a45e..aab050234c 100644 --- a/tests/many_to_one/tests.py +++ b/tests/many_to_one/tests.py @@ -406,7 +406,8 @@ class ManyToOneTests(TestCase): self.assertEqual(a3.reporter.id, self.r2.id) # Get should respect explicit foreign keys as well. - with self.assertRaises(MultipleObjectsReturned): + msg = 'get() returned more than one Article -- it returned 2!' + with self.assertRaisesMessage(MultipleObjectsReturned, msg): Article.objects.get(reporter_id=self.r.id) self.assertEqual( repr(a3), @@ -484,7 +485,11 @@ class ManyToOneTests(TestCase): setattr(c, "parent", None) # You also can't assign an object of the wrong type here - with self.assertRaises(ValueError): + msg = ( + 'Cannot assign "": "Child.parent" must ' + 'be a "Parent" instance.' + ) + with self.assertRaisesMessage(ValueError, msg): setattr(c, "parent", First(id=1, second=1)) # You can assign None to Child.parent during object creation. @@ -550,7 +555,8 @@ class ManyToOneTests(TestCase): p = Parent.objects.create(name="Parent") c = Child.objects.create(name="Child", parent=p) - with self.assertRaises(ValueError): + msg = 'Cannot assign "%r": "Child.parent" must be a "Parent" instance.' % c + with self.assertRaisesMessage(ValueError, msg): Child.objects.create(name="Grandchild", parent=c) def test_fk_instantiation_outside_model(self): diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 3e4f7b5410..e6aaa8fc90 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -757,8 +757,18 @@ class MakeMigrationsTests(MigrationTestBase): makemigrations exits if it detects a conflict. """ with self.temporary_migration_module(module="migrations.test_migrations_conflict"): - with self.assertRaises(CommandError): + with self.assertRaises(CommandError) as context: call_command("makemigrations") + exception_message = str(context.exception) + self.assertIn( + 'Conflicting migrations detected; multiple leaf nodes ' + 'in the migration graph:', + exception_message + ) + self.assertIn('0002_second', exception_message) + self.assertIn('0002_conflicting_second', exception_message) + self.assertIn('in migrations', exception_message) + self.assertIn("To fix them run 'python manage.py makemigrations --merge'", exception_message) def test_makemigrations_merge_no_conflict(self): """ @@ -780,7 +790,8 @@ class MakeMigrationsTests(MigrationTestBase): """ makemigrations exits if no app is specified with 'empty' mode. """ - with self.assertRaises(CommandError): + msg = 'You must supply at least one app label when using --empty.' + with self.assertRaisesMessage(CommandError, msg): call_command("makemigrations", empty=True) def test_makemigrations_empty_migration(self): diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index ec55ab912e..40acd5ed61 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -1945,7 +1945,7 @@ class OperationTests(OperationTestBase): operation.database_backwards("test_runpython", editor, project_state, new_state) self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0) # Now test we can't use a string - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, 'RunPython must be supplied with a callable'): migrations.RunPython("print 'ahahaha'") # And deconstruction definition = operation.deconstruct() diff --git a/tests/model_fields/test_decimalfield.py b/tests/model_fields/test_decimalfield.py index 17bbae7ffa..c8851c5d94 100644 --- a/tests/model_fields/test_decimalfield.py +++ b/tests/model_fields/test_decimalfield.py @@ -21,7 +21,8 @@ class DecimalFieldTests(TestCase): # Uses default rounding of ROUND_HALF_EVEN. self.assertEqual(f.to_python(2.0625), Decimal('2.062')) self.assertEqual(f.to_python(2.1875), Decimal('2.188')) - with self.assertRaises(ValidationError): + msg = "'abc' value must be a decimal number." + with self.assertRaisesMessage(ValidationError, msg): f.to_python('abc') def test_default(self): diff --git a/tests/model_fields/test_floatfield.py b/tests/model_fields/test_floatfield.py index c1c941b25c..481925cf11 100644 --- a/tests/model_fields/test_floatfield.py +++ b/tests/model_fields/test_floatfield.py @@ -21,8 +21,7 @@ class TestFloatField(TestCase): instance.size = instance msg = ( 'Tried to update field model_fields.FloatModel.size with a model ' - 'instance, %r. Use a value ' - 'compatible with FloatField.' + 'instance, %r. Use a value compatible with FloatField.' ) % instance with transaction.atomic(): with self.assertRaisesMessage(TypeError, msg): @@ -30,5 +29,5 @@ class TestFloatField(TestCase): # Try setting field to object on retrieved object obj = FloatModel.objects.get(pk=instance.id) obj.size = obj - with self.assertRaises(TypeError): + with self.assertRaisesMessage(TypeError, msg): obj.save() diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 578ef00696..0cfc18659f 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -180,7 +180,7 @@ class ModelFormBaseTest(TestCase): def test_no_model_class(self): class NoModelModelForm(forms.ModelForm): pass - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, 'ModelForm has no model class specified.'): NoModelModelForm() def test_empty_fields_to_fields_for_model(self): @@ -326,7 +326,7 @@ class ModelFormBaseTest(TestCase): fields = ('name', 'age') def test_extra_field_modelform_factory(self): - with self.assertRaises(FieldError): + with self.assertRaisesMessage(FieldError, 'Unknown field(s) (no-field) specified for Person'): modelform_factory(Person, fields=['no-field', 'name']) def test_replace_field(self): @@ -426,7 +426,8 @@ class ModelFormBaseTest(TestCase): form = PriceFormWithoutQuantity({'price': '6.00'}) self.assertTrue(form.is_valid()) price = form.save(commit=False) - with self.assertRaises(ValidationError): + msg = "{'quantity': ['This field cannot be null.']}" + with self.assertRaisesMessage(ValidationError, msg): price.full_clean() # The form should not validate fields that it doesn't contain even if they are @@ -498,11 +499,12 @@ class ModelFormBaseTest(TestCase): pass # no model # Can't create new form - with self.assertRaises(ValueError): + msg = 'ModelForm has no model class specified.' + with self.assertRaisesMessage(ValueError, msg): InvalidModelForm() # Even if you provide a model instance - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): InvalidModelForm(instance=Category) def test_subcategory_form(self): @@ -1301,10 +1303,11 @@ class ModelFormBasicTests(TestCase): ["Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens."] ) self.assertEqual(f.cleaned_data, {'url': 'foo'}) - with self.assertRaises(ValueError): + msg = "The Category could not be created because the data didn't validate." + with self.assertRaisesMessage(ValueError, msg): f.save() f = BaseCategoryForm({'name': '', 'slug': '', 'url': 'foo'}) - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): f.save() def test_multi_fields(self): @@ -1597,7 +1600,8 @@ class ModelChoiceFieldTests(TestCase): # instantiated. This proves clean() checks the database during clean() rather # than caching it at time of instantiation. Category.objects.get(url='4th').delete() - with self.assertRaises(ValidationError): + msg = "['Select a valid choice. That choice is not one of the available choices.']" + with self.assertRaisesMessage(ValidationError, msg): f.clean(c4.id) def test_modelchoicefield_choices(self): @@ -3045,7 +3049,11 @@ class LocalizedModelFormTest(TestCase): self.assertTrue(f.fields['right'].localize) def test_model_form_refuses_arbitrary_string(self): - with self.assertRaises(TypeError): + msg = ( + "BrokenLocalizedTripleForm.Meta.localized_fields " + "cannot be a string. Did you mean to type: ('foo',)?" + ) + with self.assertRaisesMessage(TypeError, msg): class BrokenLocalizedTripleForm(forms.ModelForm): class Meta: model = Triple diff --git a/tests/model_inheritance/test_abstract_inheritance.py b/tests/model_inheritance/test_abstract_inheritance.py index c496ac963b..f03bc3fa7e 100644 --- a/tests/model_inheritance/test_abstract_inheritance.py +++ b/tests/model_inheritance/test_abstract_inheritance.py @@ -150,10 +150,11 @@ class AbstractInheritanceTests(TestCase): def full_name(self): return self.first_name + self.last_name - with self.assertRaises(FieldDoesNotExist): + msg = "Descendant has no field named %r" + with self.assertRaisesMessage(FieldDoesNotExist, msg % 'middle_name'): Descendant._meta.get_field('middle_name') - with self.assertRaises(FieldDoesNotExist): + with self.assertRaisesMessage(FieldDoesNotExist, msg % 'full_name'): Descendant._meta.get_field('full_name') def test_overriding_field_removed_by_concrete_model(self): diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py index feff4a1407..e1eca65742 100644 --- a/tests/model_inheritance/tests.py +++ b/tests/model_inheritance/tests.py @@ -43,7 +43,7 @@ class ModelInheritanceTests(TestCase): # However, the CommonInfo class cannot be used as a normal model (it # doesn't exist as a model). - with self.assertRaises(AttributeError): + with self.assertRaisesMessage(AttributeError, "'CommonInfo' has no attribute 'objects'"): CommonInfo.objects.all() def test_reverse_relation_for_different_hierarchy_tree(self): @@ -51,7 +51,12 @@ class ModelInheritanceTests(TestCase): # Restaurant object cannot access that reverse relation, since it's not # part of the Place-Supplier Hierarchy. self.assertQuerysetEqual(Place.objects.filter(supplier__name="foo"), []) - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'supplier' into field. Choices are: " + "address, chef, chef_id, id, italianrestaurant, lot, name, " + "place_ptr, place_ptr_id, provider, rating, serves_hot_dogs, serves_pizza" + ) + with self.assertRaisesMessage(FieldError, msg): Restaurant.objects.filter(supplier__name="foo") def test_model_with_distinct_accessors(self): @@ -65,7 +70,8 @@ class ModelInheritanceTests(TestCase): # The Post model doesn't have an attribute called # 'attached_%(class)s_set'. - with self.assertRaises(AttributeError): + msg = "'Post' object has no attribute 'attached_%(class)s_set'" + with self.assertRaisesMessage(AttributeError, msg): getattr(post, "attached_%(class)s_set") def test_model_with_distinct_related_query_name(self): diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index 9edc58679a..8c4e8a8d8c 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -321,27 +321,39 @@ class QueryTestCase(TestCase): 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 "": the current database ' + 'router prevents this relation.' + ) + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='default'): marty.edited.set([pro, dive]) # Add to an m2m with an object from a different database - with self.assertRaises(ValueError): + msg = ( + 'Cannot add "": instance is on ' + 'database "default", value is on database "other"' + ) + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='default'): marty.book_set.add(dive) # Set a m2m with an object from a different database - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='default'): marty.book_set.set([pro, dive]) # Add to a reverse m2m with an object from a different database - with self.assertRaises(ValueError): + msg = ( + 'Cannot add "": instance is on ' + 'database "other", value is on database "default"' + ) + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='other'): dive.authors.add(marty) # Set a reverse m2m with an object from a different database - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='other'): dive.authors.set([mark, marty]) @@ -537,16 +549,20 @@ class QueryTestCase(TestCase): dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) # Set a foreign key with an object from a different database - with self.assertRaises(ValueError): + msg = ( + 'Cannot assign "": the current database ' + 'router prevents this relation.' + ) + with self.assertRaisesMessage(ValueError, msg): dive.editor = marty # Set a foreign key set with an object from a different database - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='default'): marty.edited.set([pro, dive]) # Add to a foreign key set with an object from a different database - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='default'): marty.edited.add(dive) @@ -655,7 +671,11 @@ class QueryTestCase(TestCase): # Set a one-to-one relation with an object from a different database alice_profile = UserProfile.objects.using('default').create(user=alice, flavor='chocolate') - with self.assertRaises(ValueError): + msg = ( + 'Cannot assign "": the ' + 'current database router prevents this relation.' + ) + with self.assertRaisesMessage(ValueError, msg): bob.userprofile = alice_profile # BUT! if you assign a FK object when the base object hasn't @@ -810,11 +830,19 @@ class QueryTestCase(TestCase): Review.objects.using('other').create(source="Python Weekly", content_object=dive) # Set a foreign key with an object from a different database - with self.assertRaises(ValueError): + msg = ( + 'Cannot assign "": the current database router ' + 'prevents this relation.' + ) + with self.assertRaisesMessage(ValueError, msg): review1.content_object = dive # Add to a foreign key set with an object from a different database - with self.assertRaises(ValueError): + msg = ( + " instance isn't saved. " + "Use bulk=False or save the object first." + ) + with self.assertRaisesMessage(ValueError, msg): with transaction.atomic(using='other'): dive.reviews.add(review1) @@ -913,11 +941,15 @@ class QueryTestCase(TestCase): # When you call __str__ on the query object, it doesn't know about using # so it falls back to the default. If the subquery explicitly uses a # different database, an error should be raised. - with self.assertRaises(ValueError): + msg = ( + "Subqueries aren't allowed across different databases. Force the " + "inner query to be evaluated using `list(inner_query)`." + ) + with self.assertRaisesMessage(ValueError, msg): str(qs.query) # Evaluating the query shouldn't work, either - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): for obj in qs: pass diff --git a/tests/null_queries/tests.py b/tests/null_queries/tests.py index 221bd05946..342b43f126 100644 --- a/tests/null_queries/tests.py +++ b/tests/null_queries/tests.py @@ -32,11 +32,12 @@ class NullQueriesTests(TestCase): self.assertSequenceEqual(Choice.objects.exclude(choice=None).order_by('id'), [c1, c2]) # Valid query, but fails because foo isn't a keyword - with self.assertRaises(FieldError): + msg = "Cannot resolve keyword 'foo' into field. Choices are: choice, id, poll, poll_id" + with self.assertRaisesMessage(FieldError, msg): Choice.objects.filter(foo__exact=None) # Can't use None on anything other than __exact and __iexact - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, 'Cannot use None as a query value'): Choice.objects.filter(id__gt=None) # Related managers use __exact=None implicitly if the object hasn't been saved. diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py index 67eed38a93..82a061b736 100644 --- a/tests/one_to_one/tests.py +++ b/tests/one_to_one/tests.py @@ -226,7 +226,11 @@ class OneToOneTests(TestCase): setattr(p, 'restaurant', None) # You also can't assign an object of the wrong type here - with self.assertRaises(ValueError): + msg = ( + 'Cannot assign "": ' + '"Place.restaurant" must be a "Restaurant" instance.' + ) + with self.assertRaisesMessage(ValueError, msg): setattr(p, 'restaurant', p) # Creation using keyword argument should cache the related object. diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py index 7be7cbbac4..85de08b184 100644 --- a/tests/prefetch_related/tests.py +++ b/tests/prefetch_related/tests.py @@ -199,14 +199,22 @@ class PrefetchRelatedTests(TestCase): def test_attribute_error(self): qs = Reader.objects.all().prefetch_related('books_read__xyz') - with self.assertRaises(AttributeError) as cm: + msg = ( + "Cannot find 'xyz' on Book object, 'books_read__xyz' " + "is an invalid parameter to prefetch_related()" + ) + with self.assertRaisesMessage(AttributeError, msg) as cm: list(qs) self.assertIn('prefetch_related', str(cm.exception)) def test_invalid_final_lookup(self): qs = Book.objects.prefetch_related('authors__name') - with self.assertRaises(ValueError) as cm: + msg = ( + "'authors__name' does not resolve to an item that supports " + "prefetching - this is an invalid parameter to prefetch_related()." + ) + with self.assertRaisesMessage(ValueError, msg) as cm: list(qs) self.assertIn('prefetch_related', str(cm.exception)) @@ -337,14 +345,22 @@ class CustomPrefetchTests(TestCase): def test_ambiguous(self): # Ambiguous: Lookup was already seen with a different queryset. - with self.assertRaises(ValueError): + msg = ( + "'houses' lookup was already seen with a different queryset. You " + "may need to adjust the ordering of your lookups." + ) + with self.assertRaisesMessage(ValueError, msg): self.traverse_qs( Person.objects.prefetch_related('houses__rooms', Prefetch('houses', queryset=House.objects.all())), [['houses', 'rooms']] ) # Ambiguous: Lookup houses_lst doesn't yet exist when performing houses_lst__rooms. - with self.assertRaises(AttributeError): + msg = ( + "Cannot find 'houses_lst' on Person object, 'houses_lst__rooms' is " + "an invalid parameter to prefetch_related()" + ) + with self.assertRaisesMessage(AttributeError, msg): self.traverse_qs( Person.objects.prefetch_related( 'houses_lst__rooms', diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 75425f86ab..47f2ece35b 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -2957,7 +2957,8 @@ class WhereNodeTest(TestCase): class QuerySetExceptionTests(TestCase): def test_iter_exceptions(self): qs = ExtraInfo.objects.only('author') - with self.assertRaises(AttributeError): + msg = "'ManyToOneRel' object has no attribute 'attname'" + with self.assertRaisesMessage(AttributeError, msg): list(qs) def test_invalid_qs_list(self): @@ -3735,9 +3736,10 @@ class TestTicket24279(TestCase): class TestInvalidValuesRelation(TestCase): def test_invalid_values(self): - with self.assertRaises(ValueError): + msg = "invalid literal for int() with base 10: 'abc'" + with self.assertRaisesMessage(ValueError, msg): Annotation.objects.filter(tag='abc') - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, msg): Annotation.objects.filter(tag__in=[123, 'abc']) diff --git a/tests/redirects_tests/tests.py b/tests/redirects_tests/tests.py index 5bce11d800..e7f5dfb97d 100644 --- a/tests/redirects_tests/tests.py +++ b/tests/redirects_tests/tests.py @@ -57,7 +57,11 @@ class RedirectTests(TestCase): @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) def test_sites_not_installed(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'You cannot use RedirectFallbackMiddleware when ' + 'django.contrib.sites is not installed.' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): RedirectFallbackMiddleware() diff --git a/tests/reverse_lookup/tests.py b/tests/reverse_lookup/tests.py index 0e0093f38e..b73b23e932 100644 --- a/tests/reverse_lookup/tests.py +++ b/tests/reverse_lookup/tests.py @@ -46,5 +46,9 @@ class ReverseLookupTests(TestCase): """ If a related_name is given you can't use the field name instead """ - with self.assertRaises(FieldError): + msg = ( + "Cannot resolve keyword 'choice' into field. Choices are: " + "creator, creator_id, id, poll_choice, question, related_choice" + ) + with self.assertRaisesMessage(FieldError, msg): Poll.objects.get(choice__name__exact="This is the answer") diff --git a/tests/select_for_update/tests.py b/tests/select_for_update/tests.py index 7228af6e8e..eaedd506de 100644 --- a/tests/select_for_update/tests.py +++ b/tests/select_for_update/tests.py @@ -246,7 +246,8 @@ class SelectForUpdateTests(TransactionTestCase): A TransactionManagementError is raised when a select_for_update query is executed outside of a transaction. """ - with self.assertRaises(transaction.TransactionManagementError): + msg = 'select_for_update cannot be used outside of a transaction.' + with self.assertRaisesMessage(transaction.TransactionManagementError, msg): list(Person.objects.all().select_for_update()) @skipUnlessDBFeature('has_select_for_update') @@ -257,7 +258,8 @@ class SelectForUpdateTests(TransactionTestCase): only when the query is executed. """ people = Person.objects.all().select_for_update() - with self.assertRaises(transaction.TransactionManagementError): + msg = 'select_for_update cannot be used outside of a transaction.' + with self.assertRaisesMessage(transaction.TransactionManagementError, msg): list(people) @skipUnlessDBFeature('supports_select_for_update_with_limit') diff --git a/tests/select_related/tests.py b/tests/select_related/tests.py index 04f762f657..c19984e267 100644 --- a/tests/select_related/tests.py +++ b/tests/select_related/tests.py @@ -137,10 +137,6 @@ class SelectRelatedTests(TestCase): .order_by('id')[0:1].get().genus.family.order.name) self.assertEqual(s, 'Diptera') - def test_depth_fields_fails(self): - with self.assertRaises(TypeError): - Species.objects.select_related('genus__family__order', depth=4) - def test_none_clears_list(self): queryset = Species.objects.select_related('genus').select_related(None) self.assertIs(queryset.query.select_related, False) diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index 1ab08cbd14..5a618954ed 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -238,7 +238,7 @@ class SettingsTests(SimpleTestCase): getattr(settings, 'TEST') def test_settings_delete_wrapped(self): - with self.assertRaises(TypeError): + with self.assertRaisesMessage(TypeError, "can't delete _wrapped."): delattr(settings, '_wrapped') def test_override_settings_delete(self): diff --git a/tests/sitemaps_tests/test_http.py b/tests/sitemaps_tests/test_http.py index 45b95d0958..b1797840b3 100644 --- a/tests/sitemaps_tests/test_http.py +++ b/tests/sitemaps_tests/test_http.py @@ -16,6 +16,10 @@ from .models import TestModel class HTTPSitemapTests(SitemapTestsBase): + use_sitemap_err_msg = ( + 'To use sitemaps, either enable the sites framework or pass a ' + 'Site/RequestSite object in your view.' + ) def test_simple_sitemap_index(self): "A simple sitemap index can be rendered" @@ -207,7 +211,7 @@ class HTTPSitemapTests(SitemapTestsBase): Sitemap.get_urls and no Site objects exist """ Site.objects.all().delete() - with self.assertRaises(ImproperlyConfigured): + with self.assertRaisesMessage(ImproperlyConfigured, self.use_sitemap_err_msg): Sitemap().get_urls() @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) @@ -217,7 +221,7 @@ class HTTPSitemapTests(SitemapTestsBase): Sitemap.get_urls if Site objects exists, but the sites framework is not actually installed. """ - with self.assertRaises(ImproperlyConfigured): + with self.assertRaisesMessage(ImproperlyConfigured, self.use_sitemap_err_msg): Sitemap().get_urls() def test_sitemap_item(self): diff --git a/tests/staticfiles_tests/test_finders.py b/tests/staticfiles_tests/test_finders.py index 20d3060092..9d5707cc2d 100644 --- a/tests/staticfiles_tests/test_finders.py +++ b/tests/staticfiles_tests/test_finders.py @@ -105,5 +105,10 @@ class TestMiscFinder(SimpleTestCase): @override_settings(MEDIA_ROOT='') def test_location_empty(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + "The storage backend of the staticfiles finder " + " " + "doesn't have a valid location." + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): finders.DefaultStorageFinder() diff --git a/tests/syndication_tests/tests.py b/tests/syndication_tests/tests.py index 28fe026414..e98ac354b0 100644 --- a/tests/syndication_tests/tests.py +++ b/tests/syndication_tests/tests.py @@ -452,7 +452,11 @@ class SyndicationFeedTest(FeedTestCase): An ImproperlyConfigured is raised if no link could be found for the item(s). """ - with self.assertRaises(ImproperlyConfigured): + msg = ( + 'Give your Article class a get_absolute_url() method, or define ' + 'an item_link() method in your Feed class.' + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.get('/syndication/articles/') def test_template_feed(self): diff --git a/tests/template_tests/syntax_tests/i18n/test_blocktrans.py b/tests/template_tests/syntax_tests/i18n/test_blocktrans.py index d8b1e807af..425e748d53 100644 --- a/tests/template_tests/syntax_tests/i18n/test_blocktrans.py +++ b/tests/template_tests/syntax_tests/i18n/test_blocktrans.py @@ -332,11 +332,13 @@ class TranslationBlockTransTagTests(SimpleTestCase): self.assertEqual(rendered, '2 andere Super-Ergebnisse') # Misuses - with self.assertRaises(TemplateSyntaxError): + msg = "Unknown argument for 'blocktrans' tag: %r." + with self.assertRaisesMessage(TemplateSyntaxError, msg % 'month="May"'): Template('{% load i18n %}{% blocktrans context with month="May" %}{{ month }}{% endblocktrans %}') - with self.assertRaises(TemplateSyntaxError): + msg = '"context" in %r tag expected exactly one argument.' % 'blocktrans' + with self.assertRaisesMessage(TemplateSyntaxError, msg): Template('{% load i18n %}{% blocktrans context %}{% endblocktrans %}') - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, msg): Template( '{% load i18n %}{% blocktrans count number=2 context %}' '{{ number }} super result{% plural %}{{ number }}' diff --git a/tests/template_tests/syntax_tests/test_filter_syntax.py b/tests/template_tests/syntax_tests/test_filter_syntax.py index 738b3ed978..176475c04c 100644 --- a/tests/template_tests/syntax_tests/test_filter_syntax.py +++ b/tests/template_tests/syntax_tests/test_filter_syntax.py @@ -43,7 +43,8 @@ class FilterSyntaxTests(SimpleTestCase): """ Raise TemplateSyntaxError for a nonexistent filter """ - with self.assertRaises(TemplateSyntaxError): + msg = "Invalid filter: 'does_not_exist'" + with self.assertRaisesMessage(TemplateSyntaxError, msg): self.engine.get_template('filter-syntax05') @setup({'filter-syntax06': '{{ var|fil(ter) }}'}) @@ -52,7 +53,7 @@ class FilterSyntaxTests(SimpleTestCase): Raise TemplateSyntaxError when trying to access a filter containing an illegal character """ - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, "Invalid filter: 'fil'"): self.engine.get_template('filter-syntax06') @setup({'filter-syntax07': "{% nothing_to_see_here %}"}) @@ -60,7 +61,11 @@ class FilterSyntaxTests(SimpleTestCase): """ Raise TemplateSyntaxError for invalid block tags """ - with self.assertRaises(TemplateSyntaxError): + msg = ( + "Invalid block tag on line 1: 'nothing_to_see_here'. Did you " + "forget to register or load this tag?" + ) + with self.assertRaisesMessage(TemplateSyntaxError, msg): self.engine.get_template('filter-syntax07') @setup({'filter-syntax08': "{% %}"}) diff --git a/tests/template_tests/syntax_tests/test_with.py b/tests/template_tests/syntax_tests/test_with.py index c1d501c94d..d8f24349f5 100644 --- a/tests/template_tests/syntax_tests/test_with.py +++ b/tests/template_tests/syntax_tests/test_with.py @@ -6,6 +6,7 @@ from ..utils import setup class WithTagTests(SimpleTestCase): + at_least_with_one_msg = "'with' expected at least one variable assignment" @setup({'with01': '{% with key=dict.key %}{{ key }}{% endwith %}'}) def test_with01(self): @@ -44,12 +45,12 @@ class WithTagTests(SimpleTestCase): @setup({'with-error01': '{% with dict.key xx key %}{{ key }}{% endwith %}'}) def test_with_error01(self): - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, self.at_least_with_one_msg): self.engine.render_to_string('with-error01', {'dict': {'key': 50}}) @setup({'with-error02': '{% with dict.key as %}{{ key }}{% endwith %}'}) def test_with_error02(self): - with self.assertRaises(TemplateSyntaxError): + with self.assertRaisesMessage(TemplateSyntaxError, self.at_least_with_one_msg): self.engine.render_to_string('with-error02', {'dict': {'key': 50}}) diff --git a/tests/template_tests/test_parser.py b/tests/template_tests/test_parser.py index cd2f2ddc05..3dc731e0a9 100644 --- a/tests/template_tests/test_parser.py +++ b/tests/template_tests/test_parser.py @@ -40,7 +40,8 @@ class ParserTests(SimpleTestCase): # Filtered variables should reject access of attributes beginning with # underscores. - with self.assertRaises(TemplateSyntaxError): + msg = "Variables and attributes may not begin with underscores: 'article._hidden'" + with self.assertRaisesMessage(TemplateSyntaxError, msg): FilterExpression("article._hidden|upper", p) def test_variable_parsing(self): @@ -64,7 +65,8 @@ class ParserTests(SimpleTestCase): # Variables should reject access of attributes beginning with # underscores. - with self.assertRaises(TemplateSyntaxError): + msg = "Variables and attributes may not begin with underscores: 'article._hidden'" + with self.assertRaisesMessage(TemplateSyntaxError, msg): Variable("article._hidden") # Variables should raise on non string type diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 06e2ce417c..80ee5a27b5 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -212,7 +212,8 @@ class AssertQuerysetEqualTests(TestCase): def test_undefined_order(self): # Using an unordered queryset with more than one ordered value # is an error. - with self.assertRaises(ValueError): + msg = 'Trying to compare non-ordered queryset against more than one ordered values' + with self.assertRaisesMessage(ValueError, msg): self.assertQuerysetEqual( Person.objects.all(), [repr(self.p1), repr(self.p2)] @@ -415,23 +416,29 @@ class AssertTemplateUsedContextManagerTests(SimpleTestCase): self.assertTemplateUsed(response, 'template_used/base.html') def test_failure(self): - with self.assertRaises(TypeError): + msg = 'response and/or template_name argument must be provided' + with self.assertRaisesMessage(TypeError, msg): with self.assertTemplateUsed(): pass - with self.assertRaises(AssertionError): + msg = 'No templates used to render the response' + with self.assertRaisesMessage(AssertionError, msg): with self.assertTemplateUsed(''): pass - with self.assertRaises(AssertionError): + with self.assertRaisesMessage(AssertionError, msg): with self.assertTemplateUsed(''): render_to_string('template_used/base.html') - with self.assertRaises(AssertionError): + with self.assertRaisesMessage(AssertionError, msg): with self.assertTemplateUsed(template_name=''): pass - with self.assertRaises(AssertionError): + msg = ( + 'template_used/base.html was not rendered. Following ' + 'templates were rendered: template_used/alternative.html' + ) + with self.assertRaisesMessage(AssertionError, msg): with self.assertTemplateUsed('template_used/base.html'): render_to_string('template_used/alternative.html') diff --git a/tests/timezones/tests.py b/tests/timezones/tests.py index 7c83ffbc47..926b8de704 100644 --- a/tests/timezones/tests.py +++ b/tests/timezones/tests.py @@ -123,7 +123,8 @@ class LegacyDatabaseTests(TestCase): @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 = 'backend does not support timezone-aware datetimes when USE_TZ is False.' + with self.assertRaisesMessage(ValueError, msg): Event.objects.create(dt=dt) def test_auto_now_and_auto_now_add(self): @@ -647,7 +648,11 @@ class UnsupportedTimeZoneDatabaseTests(TestCase): connections.databases['tz']['TIME_ZONE'] = 'Asia/Bangkok' tz_conn = connections['tz'] try: - with self.assertRaises(ImproperlyConfigured): + msg = ( + "Connection 'tz' cannot set TIME_ZONE because its engine " + "handles time zones conversions natively." + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): tz_conn.cursor() finally: connections['tz'].close() # in case the test fails @@ -1033,7 +1038,8 @@ class TemplateTests(SimpleTestCase): self.assertEqual(tpl.render(Context()), "Europe/Paris") def test_get_current_timezone_templatetag_invalid_argument(self): - with self.assertRaises(TemplateSyntaxError): + msg = "'get_current_timezone' requires 'as variable' (got ['get_current_timezone'])" + with self.assertRaisesMessage(TemplateSyntaxError, msg): Template("{% load tz %}{% get_current_timezone %}").render() @skipIf(sys.platform.startswith('win'), "Windows uses non-standard time zone names") diff --git a/tests/transaction_hooks/tests.py b/tests/transaction_hooks/tests.py index 049211139d..ed3cf18be2 100644 --- a/tests/transaction_hooks/tests.py +++ b/tests/transaction_hooks/tests.py @@ -214,7 +214,8 @@ class TestConnectionOnCommit(TransactionTestCase): try: connection.set_autocommit(False) - with self.assertRaises(transaction.TransactionManagementError): + msg = 'on_commit() cannot be used in manual transaction management' + with self.assertRaisesMessage(transaction.TransactionManagementError, msg): transaction.on_commit(should_never_be_called) finally: connection.set_autocommit(True) diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 398a14be4e..7d4d4b777a 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -299,20 +299,21 @@ class AtomicMergeTests(TransactionTestCase): class AtomicErrorsTests(TransactionTestCase): available_apps = ['transactions'] + forbidden_atomic_msg = "This is forbidden when an 'atomic' block is active." def test_atomic_prevents_setting_autocommit(self): autocommit = transaction.get_autocommit() with transaction.atomic(): - with self.assertRaises(transaction.TransactionManagementError): + with self.assertRaisesMessage(transaction.TransactionManagementError, self.forbidden_atomic_msg): transaction.set_autocommit(not autocommit) # Make sure autocommit wasn't changed. self.assertEqual(connection.autocommit, autocommit) def test_atomic_prevents_calling_transaction_methods(self): with transaction.atomic(): - with self.assertRaises(transaction.TransactionManagementError): + with self.assertRaisesMessage(transaction.TransactionManagementError, self.forbidden_atomic_msg): transaction.commit() - with self.assertRaises(transaction.TransactionManagementError): + with self.assertRaisesMessage(transaction.TransactionManagementError, self.forbidden_atomic_msg): transaction.rollback() def test_atomic_prevents_queries_in_broken_transaction(self): @@ -322,7 +323,11 @@ class AtomicErrorsTests(TransactionTestCase): with self.assertRaises(IntegrityError): r2.save(force_insert=True) # The transaction is marked as needing rollback. - with self.assertRaises(transaction.TransactionManagementError): + msg = ( + "An error occurred in the current transaction. You can't " + "execute queries until the end of the 'atomic' block." + ) + with self.assertRaisesMessage(transaction.TransactionManagementError, msg): r2.save(force_update=True) self.assertEqual(Reporter.objects.get(pk=r1.pk).last_name, "Haddock") diff --git a/tests/update/tests.py b/tests/update/tests.py index ba7ffd5c88..923af40305 100644 --- a/tests/update/tests.py +++ b/tests/update/tests.py @@ -123,7 +123,8 @@ class AdvancedTests(TestCase): We do not support update on already sliced query sets. """ method = DataPoint.objects.all()[:2].update - with self.assertRaises(AssertionError): + msg = 'Cannot update a query once a slice has been taken.' + with self.assertRaisesMessage(AssertionError, msg): method(another_value='another thing') def test_update_respects_to_field(self): diff --git a/tests/update_only_fields/tests.py b/tests/update_only_fields/tests.py index 743b5fda8d..58ae94b7cc 100644 --- a/tests/update_only_fields/tests.py +++ b/tests/update_only_fields/tests.py @@ -5,6 +5,8 @@ from .models import Account, Employee, Person, Profile, ProxyEmployee class UpdateOnlyFieldsTests(TestCase): + msg = 'The following fields do not exist in this model or are m2m fields: %s' + def test_update_fields_basic(self): s = Person.objects.create(name='Sara', gender='F') self.assertEqual(s.gender, 'F') @@ -120,7 +122,7 @@ class UpdateOnlyFieldsTests(TestCase): a2 = Account.objects.create(num=2) e1.accounts.set([a1, a2]) - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, self.msg % 'accounts'): e1.save(update_fields=['accounts']) def test_update_fields_inheritance(self): @@ -201,10 +203,12 @@ class UpdateOnlyFieldsTests(TestCase): def test_update_fields_incorrect_params(self): s = Person.objects.create(name='Sara', gender='F') - with self.assertRaises(ValueError): + with self.assertRaisesMessage(ValueError, self.msg % 'first_name'): s.save(update_fields=['first_name']) - with self.assertRaises(ValueError): + # "name" is treated as an iterable so the output is something like + # "n, a, m, e" but the order isn't deterministic. + with self.assertRaisesMessage(ValueError, self.msg % ''): s.save(update_fields="name") def test_empty_update_fields(self): diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index ff204c5650..7f565fe487 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -1067,7 +1067,12 @@ class NoRootUrlConfTests(SimpleTestCase): """Tests for handler404 and handler500 if ROOT_URLCONF is None""" def test_no_handler_exception(self): - with self.assertRaises(ImproperlyConfigured): + msg = ( + "The included URLconf 'None' does not appear to have any patterns " + "in it. If you see valid patterns in the file then the issue is " + "probably caused by a circular import." + ) + with self.assertRaisesMessage(ImproperlyConfigured, msg): self.client.get('/test/me/') diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py index 19ae51b096..3900a58247 100644 --- a/tests/user_commands/tests.py +++ b/tests/user_commands/tests.py @@ -46,7 +46,7 @@ class CommandTests(SimpleTestCase): def test_explode(self): """ An unknown command raises CommandError """ - with self.assertRaises(CommandError): + with self.assertRaisesMessage(CommandError, "Unknown command: 'explode'"): management.call_command(('explode',)) def test_system_exit(self): @@ -215,5 +215,6 @@ class CommandRunTests(AdminScriptTestCase): class UtilsTests(SimpleTestCase): def test_no_existent_external_program(self): - with self.assertRaises(CommandError): + msg = 'Error executing a_42_command_that_doesnt_exist_42' + with self.assertRaisesMessage(CommandError, msg): popen_wrapper(['a_42_command_that_doesnt_exist_42']) diff --git a/tests/validators/tests.py b/tests/validators/tests.py index 8620e7dc35..bc8ee7fb1d 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -334,7 +334,8 @@ class TestSimpleValidators(SimpleTestCase): self.assertEqual(repr(v), "ValidationError({'first': ['First Problem']})") def test_regex_validator_flags(self): - with self.assertRaises(TypeError): + msg = 'If the flags are set, regex must be a regular expression string.' + with self.assertRaisesMessage(TypeError, msg): RegexValidator(re.compile('a'), flags=re.IGNORECASE) def test_max_length_validator_message(self):