单元测试 tornado 应用程序:如何改进错误消息的显示

Unit testing tornado applications: How to improve the display of error messages

我正在使用 unittest 来测试具有多个处理程序的龙卷风应用程序,其中一个会引发异常。如果我运行下面的测试代码加上python test.py:

# test.py

import unittest
import tornado.web
import tornado.testing

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('Hello World') # handler works correctly

class HandlerWithError(tornado.web.RequestHandler):
    def get(self):
        raise Exception('Boom') # handler raises an exception
        self.write('Hello World')

def make_app():
    return tornado.web.Application([
        (r'/main/', MainHandler),
        (r'/error/', HandlerWithError),
    ])

class TornadoTestCase(tornado.testing.AsyncHTTPTestCase):

    def get_app(self):
        return make_app()

    def test_main_handler(self):
        response = self.fetch('/main/')
        self.assertEqual(response.code, 200) # test should pass

    def test_handler_with_error(self):
        response = self.fetch('/error/')
        self.assertEqual(response.code, 200) # test should fail with error

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

测试输出如下:

ERROR:tornado.application:Uncaught exception GET /error/ (127.0.0.1)                                                                                                                   
HTTPServerRequest(protocol='http', host='localhost:36590', method='GET', uri='/error/', version='HTTP/1.1', remote_ip='127.0.0.1', headers={'Connection': 'close', 'Host': 'localhost:3
6590', 'Accept-Encoding': 'gzip'})                                                                                                                                                     
Traceback (most recent call last):                                                                                                                                                     
  File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 1332, in _execute                                                                                                 
    result = method(*self.path_args, **self.path_kwargs)                                                                                                                               
  File "test.py", line 13, in get                                                                                                                                                      
    raise Exception('Boom') # handler raises an exception                                                                                                                              
Exception: Boom                                                                                                                                                                        
ERROR:tornado.access:500 GET /error/ (127.0.0.1) 19.16ms                                                                                                                               
F.                                                                                                                                                                                     
======================================================================                                                                                                                 
FAIL: test_handler_with_error (__main__.TornadoTestCase)                                                                                                                               
----------------------------------------------------------------------                                                                                                                 
Traceback (most recent call last):                                                                                                                                                     
  File "/usr/local/lib/python2.7/dist-packages/tornado/testing.py", line 118, in __call__                                                                                              
    result = self.orig_method(*args, **kwargs)                                                                                                                                         
  File "test.py", line 33, in test_handler_with_error                                                                                                                                  
    self.assertEqual(response.code, 200) # test should fail with error                                                                                                                 
AssertionError: 500 != 200                                                                                                                                                             

----------------------------------------------------------------------                                                                                                                 
Ran 2 tests in 0.034s                                                                                                                                                                  

FAILED (failures=1)       

但是,我希望 unittest 报告第二个测试的错误,而不是失败的断言。此外,'Boom' 异常的回溯出现在单元测试测试报告之前,并且不包含对失败测试函数的引用,这使得很难找到异常的来源。

有什么处理这种情况的建议吗?

提前致谢!

编辑

令我意想不到的是,test_handler_with_error 实际上是做出了 assertEqual 断言,而不是抛出错误。例如,以下代码不执行 self.assertEqual 语句,因此在测试输出中报告 ERROR 而不是 FAIL

# simple_test.py
import unittest

def foo():
    raise Exception('Boom')
    return 'bar'

class SimpleTestCase(unittest.TestCase):
    def test_failing_function(self):
        result = foo()
        self.assertEqual(result, 'bar')

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

您可以禁用logging,只显示测试报告:

logging.disable(logging.CRITICAL)

例如,您可以将其放入

  • 创建了 TestCase 子类
  • 测试运行器

更多信息How can I disable logging while running unit tests in Python Django?

请记住,CI/CD 系统实际上使用规范化报告,例如junit,然后以更多 readable/elegant 方式展示它 - 更多信息:

  • Python script to generate JUnit report from another testing result
  • How to output coverage XML with nosetests?

这是预期的行为。您的测试本身断言 return 代码是 HTTP 200,并且由于这是一个错误的正式断言,因此结果是 "failure" 而不是 "error"。您可以像 kwaranuk 的回答中提到的那样抑制日志,但是您会丢失有关实际导致 HTTP 500 错误的原因的信息。

为什么你的代码到达断言,而不是抛出?是因为你的测试代码没有调用HandlerWithError.get。您的测试代码使用 AsyncHTTPTestCase class 提供的 HTTP 客户端开始异步 HTTP GET 操作。 (查看 class 的源代码以获取详细信息。)事件循环一直运行,直到 HandlerWithError.get 通过本地主机套接字接收到请求,并在该套接字上响应 HTTP 500。当 HandlerWithError.get失败,它不会在您的测试函数中引发异常,就像 Google.com 的失败会引发异常一样:它只会导致 HTTP 500.

欢迎来到异步世界!没有简单的方法可以将断言错误和 HandlerWithError.get().

的回溯巧妙地联系起来