20

由于具有多个输出流,这是在执行具有多个独立故障模式的单个测试时出现的问题。我还想展示在所有这些模式下断言数据的结果,无论哪种模式先失败。Python 的 unittest 除了使用 Suite 来表示单个测试之外没有这样的功能,这是不可接受的,因为我的单个测试总是需要作为单个单元运行;它只是没有抓住事物的本质。

一个实际的例子是测试一个也生成日志的对象。你想断言它的方法的输出,但你也想断言日志输出。这两个输出需要不同的测试,可以巧妙地表达为两个常用的断言表达式,但您也不希望一个失败在测试中隐藏另一个可能失败。所以你真的需要同时测试两者。

我拼凑了这个有用的小部件来解决我的问题。

def logFailures(fnList):
    failurelog = []
    for fn in fnList:
        try:
            fn()
        except AssertionError as e:
            failurelog.append("\nFailure %d: %s" % (len(failurelog)+1,str(e)))

    if len(failurelog) != 0:
        raise AssertionError(
            "%d failures within test.\n %s" % (len(failurelog),"\n".join(failurelog))
        )

像这样使用:

def test__myTest():
    # do some work here
    logFailures([
        lambda: assert_(False,"This test failed."),
        lambda: assert_(False,"This test also failed."),
    ])

结果是 logFailures() 将引发一个异常,其中包含在列表中的方法中引发的所有断言的日志。

问题:虽然这可以完成工作,但我想知道是否有更好的方法来处理这个问题,而不是不得不去创建嵌套的测试套件等等?

4

3 回答 3

25

使用子测试,第一次失败后执行不会停止 https://docs.python.org/3/library/unittest.html#subtests

这是带有两个失败断言的示例:

class TestMultipleAsserts(unittest.TestCase):

    def test_multipleasserts(self):
        with self.subTest():
            self.assertEqual(1, 0)
        with self.subTest():
            self.assertEqual(2, 0)

输出将是:

======================================================================
FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test.py", line 9, in test_multipleasserts
    self.assertEqual(1, 0)
AssertionError: 1 != 0

======================================================================
FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./test.py", line 11, in test_multipleasserts
    self.assertEqual(2, 0)
AssertionError: 2 != 0

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=2)

您可以轻松包装子测试,如下所示

class MyTestCase(unittest.TestCase):
    def expectEqual(self, first, second, msg=None):
        with self.subTest():
            self.assertEqual(first, second, msg)

class TestMA(MyTestCase):
    def test_ma(self):
        self.expectEqual(3, 0)
        self.expectEqual(4, 0)
于 2017-01-25T14:16:55.930 回答
16

我不同意应该为每个断言编写测试方法的主流观点。在某些情况下,您希望在一种测试方法中检查多项内容。这是我如何做到这一点的答案:

# Works with unittest in Python 2.7
class ExpectingTestCase(unittest.TestCase):
    def run(self, result=None):
        self._result = result
        self._num_expectations = 0
        super(ExpectingTestCase, self).run(result)

    def _fail(self, failure):
        try:
            raise failure
        except failure.__class__:
            self._result.addFailure(self, sys.exc_info())

    def expect_true(self, a, msg):
        if not a:
            self._fail(self.failureException(msg))
        self._num_expectations += 1

    def expect_equal(self, a, b, msg=''):
        if a != b:
            msg = '({}) Expected {} to equal {}. '.format(self._num_expectations, a, b) + msg
            self._fail(self.failureException(msg))
        self._num_expectations += 1

以下是一些我认为它有用且没有风险的情况:

1)当您想测试不同数据集的代码时。这里我们有一个 add() 函数,我想用一些示例输入来测试它。为 3 个数据集编写 3 个测试方法意味着重复自己,这很糟糕。特别是如果电话更详细:

class MyTest(ExpectingTestCase):
    def test_multiple_inputs(self):
        for a, b, expect in ([1,1,2], [0,0,0], [2,2,4]):
            self.expect_equal(expect, add(a,b), 'inputs: {} {}'.format(a,b))

2) 当你想检查一个函数的多个输出时。我想检查每个输出,但我不希望第一次失败掩盖其他两个。

class MyTest(ExpectingTestCase):
    def test_things_with_no_side_effects(self):
        a, b, c = myfunc()
        self.expect_equal('first value', a)
        self.expect_equal('second value', b)
        self.expect_equal('third value', c)

3) 测试成本高昂的东西。测试必须快速运行,否则人们会停止使用它们。有些测试需要一个数据库或网络连接,这会真正减慢您的测试速度。如果您正在测试数据库连接本身,那么您可能需要加快速度。但是,如果您正在测试一些不相关的东西,我们希望为一整套检查进行一次慢速设置。

于 2013-01-01T01:43:45.957 回答
12

这对我来说感觉像是过度设计。任何一个:

  • 在一个测试用例中使用两个断言。如果第一个断言失败,这是真的,您将不知道第二个断言是否通过。但是无论如何你都要修复代码,所以修复它,然后你会发现第二个断言是否通过了。

  • 编写两个测试,一个检查每个条件。如果您担心测试中出现重复代码,请将大部分代码放在您从测试中调用的辅助方法中。

于 2013-01-01T03:25:57.913 回答