Python:比较unittest中的嵌套数据结构

时间:2016-03-18 19:02:09

标签: python

有没有办法比较unittest中的嵌套数据结构,但忽略具体类型的对象,如assertSequenceEqual但递归,例如:

#!/usr/bin/env python

import unittest

class DeepCompareTestCase(unittest.TestCase):

    def test_compare(self):
        # this test fails
        self.assertSequenceEqual(
            [['abc', 'def']],
            (('abc', 'def'),)
        )

unittest.main()

(类似于Perl中的Test::Deep

3 个答案:

答案 0 :(得分:1)

这样的事情会起作用吗?

import unittest
from itertools import zip_longest

class RecursiveTestCase(unittest.TestCase):
    def assertSequenceDeepEqual(self, x, y):
        MissingValue = MissingValueSentinel()
        for x, y, in zip_longest(x, y, fillvalue=MissingValue):
            try:
                self.assertSequenceDeepEqual(self, x, y)
            except TypeError:
                self.assertEqual(x, y)

                self.assertIsNot(x, MissingValue)
                self.assertIsNot(y, MissingValue)

class MissingValueSentinel(object):
    pass
如果一个或多个项目不是迭代器,则

zip_longest会引发TypeError,表示您处于递归的底部。

如果其中一个迭代器比另一个迭代器短,我让它返回一个自定义MissingValueSentinel对象,该对象由函数末尾的assertIsNot调用检测到。这样做的唯一原因是,如果xy有一个类,无论出于何种原因,它等于甚至是偶数类,如MissingValueSentinel,但assertEqual仍在在该类的两个不同对象之间有意义。这似乎是一个非常奇怪的边缘情况,你可以放心地忽略它。

使用zip_longest代替zip会阻止[1, 2, 3]错误地匹配[1, 2]

答案 1 :(得分:1)

我遇到了与您类似的问题,但就我而言,我正在测试交付给API端点的数据结构。我最终要做的是比较每个值的标准化JSON序列化。这不是一个通用的解决方案,因为它将对无法序列化为JSON的任何值引发异常,但就我而言,这是一个功能,而不是一个错误,并且确实可以在您的示例情况下使用,所以我想我愿意

我创建了一个包含以下代码的tests/assertions.py文件:

import json

class ApiAssertionsMixin(object):

    def assertJsonEqual(self, first, second, msg=None):
        j1 = json.dumps(first, sort_keys=True, indent=4)
        j2 = json.dumps(second, sort_keys=True, indent=4)
        self.maxDiff = None
        self.assertEqual(j1, j2, msg)

在您的示例中,您将像这样使用它:

import unittest
from tests.assertions import ApiAssertionsMixin

class DeepCompareTestCase(ApiAssertionsMixin, unittest.TestCase):

    def test_compare(self):
        self.assertJsonEqual(
            [['abc', 'def']],
            (('abc', 'def'),)
        )

unittest.main()

应该通过哪个。这是测试失败的示例:

    def test_deep_compare(self):
        self.assertJsonEqual(
            { 'name': 'Bob', 'aliases': ['Kate', 'Robbie'], 'age': 19 },
            { 'name': 'Bob', 'age': 20, 'aliases': ['Robbie'] }
        )

具有这样的输出:

.F
======================================================================
FAIL: test_deep_compare (__main__.DeepCompareTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_nested.py", line 18, in test_deep_compare
    { 'name': 'Bob', 'age': 20, 'aliases': ['Robbie'] }
  File "./tests/assertions.py", line 10, in assertJsonEqual
    self.assertEqual(j1, j2, msg)
AssertionError: '{\n    "age": 19,\n    "aliases": [\n        "Kate",\n [41 chars]"\n}' != '{\n    "age": 20,\n    "aliases": [\n        "Robbie"\n[24 chars]"\n}'
  {
-     "age": 19,
?            ^^
+     "age": 20,
?            ^^
      "aliases": [
-         "Kate",
          "Robbie"
      ],
      "name": "Bob"
  }

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)

如您所见,如果发生故障,您将获得足够的背景信息来确定出了什么问题。

此方法的缺点是测试的输出是JSON而不是Python,因此,如果您从输出中复制/粘贴以修复测试,则需要翻译:{{1​​}} => { {1}},true => Truefalse => False

答案 2 :(得分:0)

也许deepdiff模块有所帮助,但我没有对其进行测试。