diff --git a/django/test/testcases.py b/django/test/testcases.py index a7986dd028..46237aecd2 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -676,6 +676,11 @@ class SimpleTestCase(unittest.TestCase): msg_prefix + "Couldn't find '%s' in response" % needle) def assertJSONEqual(self, raw, expected_data, msg=None): + """ + Asserts that the JSON fragments raw and expected_data are equal. + Usual JSON non-significant whitespace rules apply as the heavyweight + is delegated to the json library. + """ try: data = json.loads(raw) except ValueError: @@ -687,6 +692,23 @@ class SimpleTestCase(unittest.TestCase): self.fail("Second argument is not valid JSON: %r" % expected_data) self.assertEqual(data, expected_data, msg=msg) + def assertJSONNotEqual(self, raw, expected_data, msg=None): + """ + Asserts that the JSON fragments raw and expected_data are not equal. + Usual JSON non-significant whitespace rules apply as the heavyweight + is delegated to the json library. + """ + try: + data = json.loads(raw) + except ValueError: + self.fail("First argument is not valid JSON: %r" % raw) + if isinstance(expected_data, six.string_types): + try: + expected_data = json.loads(expected_data) + except ValueError: + self.fail("Second argument is not valid JSON: %r" % expected_data) + self.assertNotEqual(data, expected_data, msg=msg) + def assertXMLEqual(self, xml1, xml2, msg=None): """ Asserts that two XML snippets are semantically the same. diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 35a2534792..9c8bb0c03e 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -155,6 +155,9 @@ Tests :meth:`~django.test.SimpleTestCase.assertTemplateUsed`. This allows you to assert that a template was rendered a specific number of times. +* :meth:`~django.test.SimpleTestCase.assertJSONNotEqual` new assertion allow + you to test that two JSON fragments are not equal. + Validators ^^^^^^^^^^ diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 29a94b713c..188da1bbaf 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1458,6 +1458,13 @@ your test suite. Output in case of error can be customized with the ``msg`` argument. +.. method:: SimpleTestCase.assertJSONNotEqual(raw, expected_data, msg=None) + + Asserts that the JSON fragments ``raw`` and ``expected_data`` are *not* equal. + See :meth:`~SimpleTestCase.assertJSONEqual` for further details. + + Output in case of error can be customized with the ``msg`` argument. + .. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True, msg=None) Asserts that a queryset ``qs`` returns a particular list of values ``values``. diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index e9f6da7279..1465c2be71 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -542,6 +542,51 @@ class HTMLEqualTests(TestCase): self.assertContains(response, '
Some help text for the title (with unicode ŠĐĆŽćžšđ)
', html=True) +class JSONEqualTests(TestCase): + def test_simple_equal(self): + json1 = '{"attr1": "foo", "attr2":"baz"}' + json2 = '{"attr1": "foo", "attr2":"baz"}' + self.assertJSONEqual(json1, json2) + + def test_simple_equal_unordered(self): + json1 = '{"attr1": "foo", "attr2":"baz"}' + json2 = '{"attr2":"baz", "attr1": "foo"}' + self.assertJSONEqual(json1, json2) + + def test_simple_equal_raise(self): + json1 = '{"attr1": "foo", "attr2":"baz"}' + json2 = '{"attr2":"baz"}' + with self.assertRaises(AssertionError): + self.assertJSONEqual(json1, json2) + + def test_equal_parsing_errors(self): + invalid_json = '{"attr1": "foo, "attr2":"baz"}' + valid_json = '{"attr1": "foo", "attr2":"baz"}' + with self.assertRaises(AssertionError): + self.assertJSONEqual(invalid_json, valid_json) + with self.assertRaises(AssertionError): + self.assertJSONEqual(valid_json, invalid_json) + + def test_simple_not_equal(self): + json1 = '{"attr1": "foo", "attr2":"baz"}' + json2 = '{"attr2":"baz"}' + self.assertJSONNotEqual(json1, json2) + + def test_simple_not_equal_raise(self): + json1 = '{"attr1": "foo", "attr2":"baz"}' + json2 = '{"attr1": "foo", "attr2":"baz"}' + with self.assertRaises(AssertionError): + self.assertJSONNotEqual(json1, json2) + + def test_not_equal_parsing_errors(self): + invalid_json = '{"attr1": "foo, "attr2":"baz"}' + valid_json = '{"attr1": "foo", "attr2":"baz"}' + with self.assertRaises(AssertionError): + self.assertJSONNotEqual(invalid_json, valid_json) + with self.assertRaises(AssertionError): + self.assertJSONNotEqual(valid_json, invalid_json) + + class XMLEqualTests(TestCase): def test_simple_equal(self): xml1 = "