【问题标题】:How to write a custom `.assertFoo()` method in Python?如何在 Python 中编写自定义的 `.assertFoo()` 方法?
【发布时间】:2011-07-11 19:54:38
【问题描述】:

我正在使用 Python 的 unittest 为我的应用程序编写一些测试用例。现在我需要将一个对象列表与另一个对象列表进行比较,以检查第一个列表中的对象是否符合我的预期。

如何编写自定义.assertFoo() 方法?它应该怎么做?它应该在失败时引发异常吗?如果是,哪个例外?以及如何传递错误信息?错误信息应该是 unicode 字符串还是字节字符串?

很遗憾,official documentation 没有解释如何编写自定义断言方法。

如果您需要一个真实的例子,请继续阅读。


我写的代码有点像这样:

def assert_object_list(self, objs, expected):
    for index, (item, values) in enumerate(zip(objs, expected)):
        self.assertEqual(
            item.foo, values[0],
            'Item {0}: {1} != {2}'.format(index, item.foo, values[0])
        )
        self.assertEqual(
            item.bar, values[1],
            'Item {0}: {1} != {2}'.format(index, item.bar, values[1])
        )

def test_foobar(self):
    objs = [...]  # Some processing here
    expected = [
        # Expected values for ".foo" and ".bar" for each object
        (1, 'something'),
        (2, 'nothing'),
    ]
    self.assert_object_list(objs, expected)

这种方法可以非常轻松地以非常紧凑的方式描述每个对象的预期值,而无需实际创建完整的对象。

但是...当一个对象的断言失败时,不会再比较其他对象,这使得调试更加困难。我想编写一个自定义方法,无条件比较所有对象,然后显示所有失败的对象,而不仅仅是第一个。

【问题讨论】:

  • 这个answer 问题Continuing in Python's unittest when an assertion fails 应该可以帮助您正确覆盖TestCase.assertEqual()。而另一个问题Comparison of multi-line strings in Python unit test 应该可以帮助您创建自定义myAssertEqual() 并将其注册为自动调用。

标签: python unit-testing


【解决方案1】:

在这些情况下,我使用多重继承。例如:

首先。我定义了一个类,其中包含将合并的方法。

import os

class CustomAssertions:
    def assertFileExists(self, path):
        if not os.path.lexists(path):
            raise AssertionError('File not exists in path "' + path + '".')

现在我定义一个继承自 unittest.TestCase 和 CustomAssertion 的类

import unittest

class MyTest(unittest.TestCase, CustomAssertions):
    def test_file_exists(self):
        self.assertFileExists('any/file/path')

if __name__ == '__main__':
    unittest.main()

【讨论】:

  • 很好的子类化替代方法 - 这很有帮助。
  • 这种方式也叫mixin
  • 我做了类似的事情,但我觉得很烦人的是,当测试失败时,堆栈跟踪总是显示你提出断言错误的行,而不是调用断言函数的行你的实际测试。有没有办法解决这个问题?
  • @CristóbalGanter 修复堆栈跟踪中的行见stackoverflow.com/questions/49927872/…
  • 表示测试失败的推荐方法是致电self.fail() 或等效的raise self.failureException(msg)。虽然 raise 和 AssertionError 现在可以工作,但它可能并不总是被 unittest 框架同等对待:参见docs.python.org/2/library/unittest.html#re-using-old-test-code
【解决方案2】:

您应该创建自己的 TestCase 类,从 unittest.TestCase 派生。然后将您的自定义断言方法放入该测试用例类中。如果您的测试失败,请引发 AssertionError。您的消息应该是一个字符串。如果您想测试列表中的所有对象而不是在失败时停止,则收集失败索引的列表,并在遍历所有对象之后,构建一个断言消息来总结您的发现。

【讨论】:

  • 提出AssertionError 和任何其他Exception 有什么区别(关于自定义断言和unittest)?
【解决方案3】:

只是一个例子来总结一个numpy比较单元测试

import numpy as np
class CustomTestCase(unittest.TestCase):
    def npAssertAlmostEqual(self, first, second, rtol=1e-06, atol=1e-08):
        np.testing.assert_allclose(first, second, rtol=rtol, atol=atol)


class TestVector(CustomTestCase):
    def testFunction(self):
        vx = np.random.rand(size)
        vy = np.random.rand(size)
        self.npAssertAlmostEqual(vx, vy)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多