从python中的单元测试输出数据

Sil*_*ish 108 python unit-testing

如果我在python中编写单元测试(使用unittest模块),是否可以从失败的测试中输出数据,所以我可以检查它以帮助推断导致错误的原因?我知道能够创建自定义消息,它可以携带一些信息,但有时您可能会处理更复杂的数据,这些数据不能轻易地表示为字符串.

例如,假设您有一个类Foo,并使用名为testdata的列表中的数据测试方法栏:

class TestBar(unittest.TestCase):
    def runTest(self):
        for t1, t2 in testdata:
            f = Foo(t1)
            self.assertEqual(f.bar(t2), 2)
Run Code Online (Sandbox Code Playgroud)

如果测试失败,我可能想输出t1,t2和/或f,以查看此特定数据导致失败的原因.通过输出,我的意思是在运行测试之后,可以像任何其他变量一样访问变量.

Fac*_*sco 70

对于像我这样的人来到这里寻找一个简单快速的答案的人来说,这是一个很晚的答案.

在Python 2.7中,您可以使用其他参数msg向错误消息添加信息,如下所示:

self.assertEqual(f.bar(t2), 2, msg='{0}, {1}'.format(t1, t2))
Run Code Online (Sandbox Code Playgroud)

官方文档在这里

  • 文档暗示了这一点,但值得明确提及:默认情况下,如果使用`msg`,它将替换正常的错误消息.要将`msg`附加到正常错误消息,还需要将[TestCase.longMessage](https://docs.python.org/2/library/unittest.html#unittest.TestCase.longMessage)设置为True (17认同)
  • @CatalinIacob的评论适用于Python 2.x. 在Python 3.x中,[TestCase.longMessage](https://docs.python.org/3/library/unittest.html#unittest.TestCase.longMessage)默认为"True". (3认同)
  • 也适用于 Python 3。 (2认同)
  • 很高兴知道我们可以传递自定义错误消息,但我对打印一些消息很感兴趣,而不管错误如何。 (2认同)

S.L*_*ott 68

我们使用日志记录模块.

例如:

import logging
class SomeTest( unittest.TestCase ):
    def testSomething( self ):
        log= logging.getLogger( "SomeTest.testSomething" )
        log.debug( "this= %r", self.this )
        log.debug( "that= %r", self.that )
        # etc.
        self.assertEquals( 3.14, pi )

if __name__ == "__main__":
    logging.basicConfig( stream=sys.stderr )
    logging.getLogger( "SomeTest.testSomething" ).setLevel( logging.DEBUG )
    unittest.main()
Run Code Online (Sandbox Code Playgroud)

这允许我们打开我们知道失败的特定测试的调试,并且我们需要额外的调试信息.

但是,我首选的方法是不要花费大量时间进行调试,而是花费大量时间编写更精细的测试来揭示问题.

  • 我按照您指定的方式设置日志记录.我认为它有效,但我在哪里可以看到输出?它没有输出到控制台.我尝试通过记录到文件来配置它,但这也不会产生任何输出. (6认同)

Ned*_*der 32

您可以使用简单的打印语句或任何其他写入stdout的方式.您还可以在测试中的任何位置调用Python调试器.

如果您使用nose运行测试(我建议),它将收集每个测试的标准输出,并且只有在测试失败时才显示给您,因此在测试通过时您不必使用杂乱的输出.

nose还具有自动显示断言中提到的变量的开关,或者在失败的测试中调用调试器.例如-s(--nocapture)阻止stdout的捕获.

  • `nosetests -s`显示stdout的内容是否有错误 - 我发现有用的东西. (6认同)

mon*_*kut 16

我认为这不是你想要的,没有办法显示不会失败的变量值,但这可能会帮助你更接近按照你想要的方式输出结果.

您可以使用TestRunner.run()返回的TestResult对象进行结果分析和处理.特别是TestResult.errors和TestResult.failures

关于TestResults对象:

http://docs.python.org/library/unittest.html#id3

还有一些代码可以指出你正确的方向:

>>> import random
>>> import unittest
>>>
>>> class TestSequenceFunctions(unittest.TestCase):
...     def setUp(self):
...         self.seq = range(5)
...     def testshuffle(self):
...         # make sure the shuffled sequence does not lose any elements
...         random.shuffle(self.seq)
...         self.seq.sort()
...         self.assertEqual(self.seq, range(10))
...     def testchoice(self):
...         element = random.choice(self.seq)
...         error_test = 1/0
...         self.assert_(element in self.seq)
...     def testsample(self):
...         self.assertRaises(ValueError, random.sample, self.seq, 20)
...         for element in random.sample(self.seq, 5):
...             self.assert_(element in self.seq)
...
>>> suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
>>> testResult = unittest.TextTestRunner(verbosity=2).run(suite)
testchoice (__main__.TestSequenceFunctions) ... ERROR
testsample (__main__.TestSequenceFunctions) ... ok
testshuffle (__main__.TestSequenceFunctions) ... FAIL

======================================================================
ERROR: testchoice (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 11, in testchoice
ZeroDivisionError: integer division or modulo by zero

======================================================================
FAIL: testshuffle (__main__.TestSequenceFunctions)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 8, in testshuffle
AssertionError: [0, 1, 2, 3, 4] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

----------------------------------------------------------------------
Ran 3 tests in 0.031s

FAILED (failures=1, errors=1)
>>>
>>> testResult.errors
[(<__main__.TestSequenceFunctions testMethod=testchoice>, 'Traceback (most recent call last):\n  File "<stdin>"
, line 11, in testchoice\nZeroDivisionError: integer division or modulo by zero\n')]
>>>
>>> testResult.failures
[(<__main__.TestSequenceFunctions testMethod=testshuffle>, 'Traceback (most recent call last):\n  File "<stdin>
", line 8, in testshuffle\nAssertionError: [0, 1, 2, 3, 4] != [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n')]
>>>
Run Code Online (Sandbox Code Playgroud)


Sil*_*ish 5

我想我可能一直在思考这个问题.我提出的一种方法就是完成工作,只需要一个全局变量来累积诊断数据.

像这样的东西:

log1 = dict()
class TestBar(unittest.TestCase):
    def runTest(self):
        for t1, t2 in testdata:
            f = Foo(t1) 
            if f.bar(t2) != 2: 
                log1("TestBar.runTest") = (f, t1, t2)
                self.fail("f.bar(t2) != 2")
Run Code Online (Sandbox Code Playgroud)

谢谢你的回复.他们给了我一些关于如何记录单元测试信息的替代方法.


ori*_*rip 5

另一种选择 - 启动测试失败的调试器.

尝试使用Testoob运行测试(它将运行您的unittest套件而不进行更改),并且您可以使用'--debug'命令行开关在测试失败时打开调试器.

这是windows上的终端会话:

C:\work> testoob tests.py --debug
F
Debugging for failure in test: test_foo (tests.MyTests.test_foo)
> c:\python25\lib\unittest.py(334)failUnlessEqual()
-> (msg or '%r != %r' % (first, second))
(Pdb) up
> c:\work\tests.py(6)test_foo()
-> self.assertEqual(x, y)
(Pdb) l
  1     from unittest import TestCase
  2     class MyTests(TestCase):
  3       def test_foo(self):
  4         x = 1
  5         y = 2
  6  ->     self.assertEqual(x, y)
[EOF]
(Pdb)
Run Code Online (Sandbox Code Playgroud)

  • Nose(http://nose.readthedocs.org/en/latest/index.html)是另一个提供"启动调试器会话"选项的框架.我使用'-sx --pdb --pdb-failures'运行它,它不吃掉输出,在第一次失败后停止,并在异常和测试失败时掉入pdb.这消除了我对丰富错误消息的需求,除非我懒惰并在循环中进行测试. (2认同)

Ora*_*ane 5

我使用的方法非常简单.我只是将其记录为警告,以便它实际显示出来.

import logging

class TestBar(unittest.TestCase):
    def runTest(self):

       #this line is important
       logging.basicConfig()
       log = logging.getLogger("LOG")

       for t1, t2 in testdata:
         f = Foo(t1)
         self.assertEqual(f.bar(t2), 2)
         log.warning(t1)
Run Code Online (Sandbox Code Playgroud)


fed*_*qui 5

在这些情况下,我log.debug()在我的应用程序中使用了一些消息。由于默认日志级别为WARNING,因此在正常执行中不会显示此类消息。

然后,在单元测试中,我将日志记录级别更改为DEBUG,以便在运行它们时显示此类消息。

import logging

log.debug("Some messages to be shown just when debugging or unit testing")
Run Code Online (Sandbox Code Playgroud)

在单元测试中:

# Set log level
loglevel = logging.DEBUG
logging.basicConfig(level=loglevel)
Run Code Online (Sandbox Code Playgroud)


查看完整示例:

这是daikiri.py一个基本类,它实现一个daikiri其名称和价格。有一种方法make_discount()可以在应用给定的折扣后返回该特定代基里的价格:

import logging

log = logging.getLogger(__name__)

class Daikiri(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def make_discount(self, percentage):
        log.debug("Deducting discount...")  # I want to see this message
        return self.price * percentage
Run Code Online (Sandbox Code Playgroud)

然后,我创建了一个单元测试test_daikiri.py,来检查它的使用情况:

import unittest
import logging
from .daikiri import Daikiri


class TestDaikiri(unittest.TestCase):
    def setUp(self):
        # Changing log level to DEBUG
        loglevel = logging.DEBUG
        logging.basicConfig(level=loglevel)

        self.mydaikiri = Daikiri("cuban", 25)

    def test_drop_price(self):
        new_price = self.mydaikiri.make_discount(0)
        self.assertEqual(new_price, 0)

if __name__ == "__main__":
    unittest.main()
Run Code Online (Sandbox Code Playgroud)

因此,当我执行它时,我收到log.debug消息:

$ python -m test_daikiri
DEBUG:daikiri:Deducting discount...
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Run Code Online (Sandbox Code Playgroud)