![](/img/trans.png)
[英]Can I handle multiple asserts within a single Python pytest method?
[英]How do I handle multiple asserts within a single Python unittest?
由于具有多个输出流,因此在执行具有多个独立故障模式的单个测试时会出现此问题。 我还想展示在所有那些模式下断言数据的结果,而不管哪个先失败。 Python的单元测试除了使用套件表示单个测试外没有其他功能,这是不可接受的,因为我的单个测试始终需要作为一个单元运行。 它只是没有抓住事物的本质。
一个实际的示例是测试也会生成日志的对象。 您想声明其方法的输出,但也想声明日志的输出。 这两个输出需要不同的测试,可以将它们很好地表示为两个断言表达式,但是您也不希望一个失败会在测试中隐藏另一个可能的失败。 因此,您确实需要同时测试两者。
我拼凑了这个有用的小部件来解决我的问题。
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()将引发一个异常,该异常包含列表中方法中引发的所有断言的日志。
问题:尽管这样做可以完成工作,但我想知道是否还有更好的方法来处理此问题,而不是花很多时间来创建嵌套的测试套件等等?
我不同意一种普遍观点,即应该为每个断言编写一种测试方法。 在某些情况下,您想用一种测试方法检查多件事情。 这是我的解决方法:
# 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)以高昂的安装成本进行测试。 测试必须快速运行,否则人们将停止使用它们。 某些测试需要花费一秒钟的数据库或网络连接,这实际上会降低测试速度。 如果要测试数据库连接本身,则可能需要提高速度。 但是,如果您要测试不相关的内容,则我们希望对整套检查进行一次缓慢的设置。
使用子测试时,在第一次失败后,执行不会停止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)
对我来说,这感觉像是过度设计。 要么:
在一个测试用例中使用两个断言。 如果第一个断言失败,那是事实,您将不知道第二个断言是否通过。 但是无论如何,您都将要修复该代码,因此对其进行修复,然后您将确定第二个断言是否通过。
编写两个测试,一个检查每个条件。 如果您担心测试中的代码重复,请将大部分代码放在从测试中调用的帮助器方法中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.