tornadoweb / tornadoweb/tornado

AsyncHTTPTestCase's fetch swallows httpclient exception

Open
#1,665 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

testing
Dominant language
Python
Stars
22.2k
Forks
5.6k
Avg merge
3h 42m
Merged PRs (30d)
16

Description

Python: 3.5.1
Tornado: 4.3
Ubuntu 12.04

Case:

import unittest
from tornado import testing
from tornado import web
from tornado import gen

class Handler(web.RequestHandler):

    @gen.coroutine
    def post(self):
        self.write('spam')


class HandlerTest(testing.AsyncHTTPTestCase):
    def get_app(self):
        return web.Application([('/', Handler)])

    def test_post(self):
        response = self.fetch('/', method='POST')
        self.assertEqual(b'spam', response.body)

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

Produces:

F
======================================================================
FAIL: test_post (__main__.HandlerTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/t35/lib/python3.5/site-packages/tornado/testing.py", line 132, in __call__
    result = self.orig_method(*args, **kwargs)
  File "test.py", line 20, in test_post
    self.assertEqual(b'spam', response.body)
AssertionError: b'spam' != None

----------------------------------------------------------------------
Ran 1 test in 0.005s

FAILED (failures=1)

instead of "meaningful" error.

If changed to @gen_test, it produce proper error:

import unittest
from tornado import testing
from tornado import web
from tornado import gen
from tornado.testing import gen_test

class Handler(web.RequestHandler):

    @gen.coroutine
    def post(self):
        self.write('spam')


class HandlerTest(testing.AsyncHTTPTestCase):
    def get_app(self):
        return web.Application([('/', Handler)])

    @gen_test
    def test_post(self):
        response = self.http_client.fetch(self.get_url('/'), method='POST')
        self.assertEqual(b'spam', response.body)

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

and the error, that is ok:

E
======================================================================
ERROR: test_post (__main__.HandlerTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/t35/lib/python3.5/site-packages/tornado/testing.py", line 132, in __call__
    result = self.orig_method(*args, **kwargs)
  File "/tmp/t35/lib/python3.5/site-packages/tornado/testing.py", line 525, in post_coroutine
    timeout=timeout)
  File "/tmp/t35/lib/python3.5/site-packages/tornado/ioloop.py", line 453, in run_sync
    return future_cell[0].result()
  File "/tmp/t35/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
  File "/tmp/t35/lib/python3.5/site-packages/tornado/gen.py", line 267, in wrapper
    result = func(*args, **kwargs)
  File "/tmp/t35/lib/python3.5/types.py", line 243, in wrapped
    coro = func(*args, **kwargs)
  File "/tmp/t35/lib/python3.5/site-packages/tornado/testing.py", line 508, in pre_coroutine
    result = f(self, *args, **kwargs)
  File "test.py", line 22, in test_post
    self.assertEqual(b'spam', response.body)
AttributeError: 'Future' object has no attribute 'body'

----------------------------------------------------------------------
Ran 1 test in 0.006s

FAILED (errors=1)
ERROR:tornado.application:Future <tornado.concurrent.Future object at 0x7ffb966965c0> exception was never retrieved: Traceback (most recent call last):
  File "/tmp/t35/lib/python3.5/site-packages/tornado/stack_context.py", line 314, in wrapped
    ret = fn(*args, **kwargs)
  File "/tmp/t35/lib/python3.5/site-packages/tornado/gen.py", line 264, in <lambda>
    future, lambda future: callback(future.result()))
  File "/tmp/t35/lib/python3.5/site-packages/tornado/simple_httpclient.py", line 353, in _on_connect
    ('not ' if body_expected else '', self.request.method))
ValueError: Body must not be None for method POST (unless allow_nonstandard_methods is true)

Ref. http://stackoverflow.com/questions/36013805/can-only-perform-get-requests-in-tornado-test/36015750#36015750

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the shown test_post case, then inspect AsyncHTTPTestCase.fetch in tornado/testing.py and the exception path in tornado/simple_httpclient.py. Add regression coverage for this failure and verify that fetch surfaces the underlying HTTP-client error instead of returning a response with body set to None.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.