断言两个词典几乎相同

Aka*_*all 12 python dictionary assert nose

我试图断言两个词典几乎是平等的,但我似乎无法做到这一点.

这是一个例子:

>>> import nose.tools as nt
>>> nt.assert_dict_equal({'a' : 12.4}, {'a' : 5.6 + 6.8})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/unittest/case.py", line 838, in assertDictEqual
    self.fail(self._formatMessage(msg, standardMsg))
  File "/usr/lib/python2.7/unittest/case.py", line 413, in fail
    raise self.failureException(msg)
AssertionError: {'a': 12.4} != {'a': 12.399999999999999}
- {'a': 12.4}
+ {'a': 12.399999999999999}
Run Code Online (Sandbox Code Playgroud)

我希望这会通过,就像那样:

>>> nt.assert_almost_equal(12.4, 5.6 + 6.8)
Run Code Online (Sandbox Code Playgroud)

我希望我遗漏一些简单的东西nt.assert_almost_dict_equal,或者也许我可以传递的参数nt.assert_dict_equal指定浮点数应该有多近,但我找不到任何东西.

当然,我可以循环遍历字典并使用nt.assert_almost_equal单独比较值; 但是,在我的情况下,字典更复杂,所以我希望避免这样.

断言两个字典几乎相等的最佳方法是什么?

Aka*_*all 15

@dano的评论回答了我的问题:

我从dano提供链接中复制了一个函数

import unittest
import numpy

def assertDeepAlmostEqual(test_case, expected, actual, *args, **kwargs):
    """
    Assert that two complex structures have almost equal contents.

    Compares lists, dicts and tuples recursively. Checks numeric values
    using test_case's :py:meth:`unittest.TestCase.assertAlmostEqual` and
    checks all other values with :py:meth:`unittest.TestCase.assertEqual`.
    Accepts additional positional and keyword arguments and pass those
    intact to assertAlmostEqual() (that's how you specify comparison
    precision).

    :param test_case: TestCase object on which we can call all of the basic
    'assert' methods.
    :type test_case: :py:class:`unittest.TestCase` object
    """
    is_root = not '__trace' in kwargs
    trace = kwargs.pop('__trace', 'ROOT')
    try:
        if isinstance(expected, (int, float, long, complex)):
            test_case.assertAlmostEqual(expected, actual, *args, **kwargs)
        elif isinstance(expected, (list, tuple, numpy.ndarray)):
            test_case.assertEqual(len(expected), len(actual))
            for index in xrange(len(expected)):
                v1, v2 = expected[index], actual[index]
                assertDeepAlmostEqual(test_case, v1, v2,
                                      __trace=repr(index), *args, **kwargs)
        elif isinstance(expected, dict):
            test_case.assertEqual(set(expected), set(actual))
            for key in expected:
                assertDeepAlmostEqual(test_case, expected[key], actual[key],
                                      __trace=repr(key), *args, **kwargs)
        else:
            test_case.assertEqual(expected, actual)
    except AssertionError as exc:
        exc.__dict__.setdefault('traces', []).append(trace)
        if is_root:
            trace = ' -> '.join(reversed(exc.traces))
            exc = AssertionError("%s\nTRACE: %s" % (exc.message, trace))
        raise exc

# My part, using the function

class TestMyClass(unittest.TestCase):
    def test_dicts(self):
        assertDeepAlmostEqual(self, {'a' : 12.4}, {'a' : 5.6 + 6.8})
    def test_dicts_2(self):
        dict_1 = {'a' : {'b' : [12.4, 0.3]}}
        dict_2 = {'a' : {'b' : [5.6 + 6.8, 0.1 + 0.2]}}

        assertDeepAlmostEqual(self, dict_1, dict_2)

def main():
    unittest.main()

if __name__ == "__main__":
    main()
Run Code Online (Sandbox Code Playgroud)

结果:

Ran 2 tests in 0.000s

OK
Run Code Online (Sandbox Code Playgroud)

  • Downvoter,你能解释一下downvote的原因吗?我相信自我答案很好.你认为我没有给@dano足够的信用吗? (3认同)
  • 这可能是不好的风格,但是如果您通过“unittest.TestCase.assertDeepAlmostEqual = assertDeepAlmostEqual”对 TestCase 进行猴子补丁,那么您可以像其他测试一样使用该测试,例如“self.assertDeepAlmostEqual(dict_1, dict_2)” (2认同)
  • 无需猴子修补“TestCase”。您可以简单地对它进行子类化。事实上,这就是“testtools.TestCase”所做的。 (2认同)
  • Python 2 不再受支持,因此我将用“range”替换“xrange”。另外,“long”应该是“numpy.long”,或者您可以显式“from numpy import long, ndarray”并保存导入所有 numpy。 (2认同)