From 8316582f8df6b2251b10e2764577ecaee84718f9 Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Thu, 30 Jul 2015 07:03:14 -0400 Subject: [PATCH] return more details on assertJsonEqual fail assertJsonEqual does a recursive traverse of a data structure, using assertEqual strategically along the way. The problem with this is that it can lead to really cryptic messages like Failed '3' != '4' When some very deep list was the wrong number of elements. It doesn't tell you which list was the wrong number of elements, and so it's completely opaque to get to the bottom of. Especially if this doesn't happen on every run, just some. This creates a version which catches an inner match issue, and constructs a new failure which includes information about the 2 data structures being compared at the top level. This should help debugging racey unit test in the gate like test_describe_instances_with_filters_tags. Related-Bug: #1479780 Change-Id: I6f6e961a3c63f9f86fe9b21ce6c16a2e634e9ce5 --- nova/test.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/nova/test.py b/nova/test.py index 44dede65d..62819943b 100644 --- a/nova/test.py +++ b/nova/test.py @@ -289,6 +289,21 @@ class TestCase(testtools.TestCase): return svc.service def assertJsonEqual(self, expected, observed): + """Asserts that 2 complex data structures are json equivalent. + + We use data structures which serialize down to json throughout + the code, and often times we just need to know that these are + json equivalent. This means that list order is not important, + and should be sorted. + + Because this is a recursive set of assertions, when failure + happens we want to expose both the local failure and the + global view of the 2 data structures being compared. So a + MismatchError which includes the inner failure as the + mismatch, and the passed in expected / observed as matchee / + matcher. + + """ if isinstance(expected, six.string_types): expected = jsonutils.loads(expected) if isinstance(observed, six.string_types): @@ -325,7 +340,15 @@ class TestCase(testtools.TestCase): else: self.assertEqual(expected, observed) - inner(expected, observed) + try: + inner(expected, observed) + except testtools.matchers.MismatchError as e: + inner_mismatch = e.mismatch + # inverting the observed / expected because testtools + # error messages assume expected is second. Possibly makes + # reading the error messages less confusing. + raise testtools.matchers.MismatchError(observed, expected, + inner_mismatch, verbose=True) def assertPublicAPISignatures(self, baseinst, inst): def get_public_apis(inst):