断言失败时继续在Python的单元测试中

问题描述 投票:77回答:12

编辑:切换到一个更好的示例,并阐明了为什么这是一个真正的问题。

我想用Python编写单元测试,当断言失败时,该单元测试将继续执行,以便在一个测试中可以看到多个失败。例如:

class Car(object):
  def __init__(self, make, model):
    self.make = make
    self.model = make  # Copy and paste error: should be model.
    self.has_seats = True
    self.wheel_count = 3  # Typo: should be 4.

class CarTest(unittest.TestCase):
  def test_init(self):
    make = "Ford"
    model = "Model T"
    car = Car(make=make, model=model)
    self.assertEqual(car.make, make)
    self.assertEqual(car.model, model)  # Failure!
    self.assertTrue(car.has_seats)
    self.assertEqual(car.wheel_count, 4)  # Failure!

这里,测试的目的是确保Car的__init__正确设置其字段。我可以将其分解为四个方法(这通常是个好主意),但在这种情况下,我认为将其保留为测试单个概念的单个方法(“对象已正确初始化”)更容易理解。

如果我们假设最好不要破坏该方法,那么我有一个新问题:我无法一次看到所有错误。当我修复model错误并重新运行测试时,会出现wheel_count错误。当我第一次运行测试时,这将节省我看到两个错误的时间。

作为比较,Google的C ++单元测试框架distinguishes between在非致命EXPECT_*断言与致命ASSERT_*断言之间:

断言成对出现,它们测试相同的事物,但对当前函数有不同的影响。 ASSERT_ *版本失败时会产生致命错误,并中止当前功能。 EXPECT_ *版本会产生非致命故障,不会导致当前功能终止。通常首选EXPECT_ *,因为它们允许在测试中报告多个故障。但是,如果在所声明的断言失败后继续运行没有意义,则应使用ASSERT _ *。

是否有办法在Python的EXPECT_*中获得类似unittest的行为?如果不在unittest中,那么是否存在另一个支持此行为的Python单元测试框架?


[顺便说一句,我很好奇非致命性断言可能会带来多少实际测试,因此我看了一些code examples(2014年8月19日编辑,使用搜索代码而不是Google Code Search,RIP)。从第一页随机选择的10个结果中,所有包含的测试都在同一测试方法中做出了多个独立的断言。所有人将从非致命的断言中受益。

编辑:切换到一个更好的示例,并阐明了为什么这是一个真正的问题。我想用Python编写在断言失败时继续执行的单元测试,以便可以看到多个...

python unit-testing
12个回答
8
投票

您可能想要做的是派生unittest.TestCase,因为这是断言失败时抛出的类。您将不得不重新构造您的TestCase以便不抛出错误(或者保留故障列表)。重新架构内容可能会导致其他必须解决的问题。例如,您可能最终需要派生TestSuite来进行更改以支持对TestCase所做的更改。


0
投票

我对model的答案有疑问,因为它迫使我在单元测试中使用FF ====================================================================== FAIL: test_creation_defaults (__main__.CarTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_car.py", line 25, in test_creation_defaults self.assertEqual(self.car.wheel_count, 4) # Failure! AssertionError: 3 != 4 ====================================================================== FAIL: test_creation_parameters (__main__.CarTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_car.py", line 20, in test_creation_parameters self.assertEqual(self.car.model, self.model) # Failure! AssertionError: 'Ford' != 'Model T' ---------------------------------------------------------------------- Ran 2 tests in 0.000s FAILED (failures=2) 。然后,我将@Anthony Batchelor逻辑封装在try...catch方法的替代中。以下技巧从单元测试代码中删除了try...catch块:


0
投票

我知道这个问题是在几年前提出的,但是现在(至少)有两个Python软件包可让您执行此操作。


0
投票

自Python 3.4起,您还可以使用https://pypi.org/project/softest/


39
投票

具有非致命断言的另一种方法是捕获断言异常并将异常存储在列表中。然后断言该列表是空的,作为tearDown的一部分。


29
投票

一个选项同时作为一个元组对所有值进行断言。


8
投票

在单个单元测试中具有多个断言被认为是反模式。单个单元测试只能测试一件事。也许您正在测试太多。考虑将此测试分成多个测试。这样,您可以正确命名每个测试。


5
投票

每个断言都使用单独的方法。


2
投票

我喜欢@ Anthony-Batchelor的方法来捕获AssertionError异常。但是使用装饰器的这种方法略有变化,并且还是通过/失败报告测试案例的方法。


2
投票

expect在gtest中非常有用。这是gist中的python方式,代码:


2
投票

PyPI中有一个名为softest的软断言包,可以满足您的要求。它通过收集故障,组合异常和堆栈跟踪数据并将其全部报告为常规softest输出的一部分来工作。


0
投票

我不认为可以用PyUnit做到这一点,也不想看到PyUnit以这种方式扩展。

© www.soinside.com 2019 - 2024. All rights reserved.