如何编写可在 @tornado.testing.gen_test 中调用的函数?

3 投票
1 回答
690 浏览
提问于 2025-04-18 07:37

我有一些代码想要测试,但遇到了一些麻烦,这个麻烦跟使用@tornado.testing.gen_test这个测试工具有关,它需要用到一种叫生成器的东西:

class GameTest(tornado.testing.AsyncHTTPTestCase):

    def new_game(self):
        ws = yield websocket_connect('address')
        ws.write_message('new_game')
        response = yield ws.read_message()
        # I want to say:
        # return response

    @tornado.testing.gen_test
    def test_new_game(self):
        response = self.new_game()
        # do some testing

问题是,我不能从生成器中返回一个值,所以我本能的想法是错的。此外,我不能这样做:

class GameTest(tornado.testing.AsyncHTTPTestCase):

    def new_game(self):
        ws = yield websocket_connect('address')
        ws.write_message('new_game')
        response = yield ws.read_message()
        yield response, True

    @tornado.testing.gen_test
    def test_new_game(self):
        for i in self.new_game():
            if isinstance(i, tuple):
                response, success = i
                break
        # do some testing

因为这样我会遇到错误:

AttributeError: 'NoneType' object has no attribute 'write_message'

显然,我可以把整个测试生成的代码放在测试里面,但这样做真的很麻烦,维护起来也很困难等等。难道这个测试模式真的让事情变得这么复杂吗?

1 个回答

5

在异步函数中,你应该使用 @gen.coroutine,这样这些函数才能被 @gen_test 方法调用,就像在普通代码中一样。@gen_test 是一个适配器,它让你可以在同步的单元测试界面中使用异步代码。

@gen.coroutine
def new_game(self):
    ws = yield websocket_connect('address')
    ws.write_message('new_game')
    response = yield ws.read_message()
    raise gen.Return(response)

@tornado.testing.gen_test
def test_new_game(self):
    response = yield self.new_game()
    # do some testing

在 Python 3.3 及以上版本中,你可以用 return response 来代替 raise gen.Return(response)。如果你在调用的地方使用 yield from,甚至可以省略 @gen.coroutine

撰写回答