2015-04-21 4 views
1

наблюдал ajdavis @ pycon2015 о тестировании асинхронных приложениях, как показано ниже: http://pyvideo.org/video/3419/eventually-correct-testing-async-appsExceptionStackContext для тестирования Торнадо асинхронного приложения

У меня есть вопрос об использовании ExceptionStackContext. В презентации, он используется, как показано ниже:

import unittest 
from tornado.stack_context import ExceptionStackContext 
from tornado.ioloop import IOLoop 

class TestAsync(unittest.TestCase): 
    def test_delay(self): 
     io_loop = IOLoop.current() 
     def handle_exception(typ, val, tb): 
      self.failure = (typ, val, tb) 
      io_loop.stop() 
     def done(): 
      self.assertAlmostEqual(time.time() - start, 2) 
      self.stop() 
     with ExceptionStackContext(handle_exception): 
      delay(3, done) # fail the assert 
     io_loop.start() 

запустить этот тест, io_loop не остановится. потому что handle_exception не вызывается.

мой метод асинхронной delay является:

import threading 
import time 

def delay(delay_seconds, callback): 
    def wrap():  
     time.sleep(delay_seconds) 
     callback() 
    t = threading.Thread(target=wrap) 
    t.daemon = True 
    t.start() 

так я думаю, он должен ExceptionStackContext должен обернуть done(), как показано ниже:

def callback(): 
    with ExceptionStackContext(handle_exception): 
     done() 
delay(2, callback) 
io_loop.start() 

это путь райт использовать ExceptionStackContext?

, кстати, ExceptionStackContext в tornado.testing.AsyncTestCase бесполезно на самом деле:

def run(self, result=None): 
    with ExceptionStackContext(self._handle_exception): 
     super(AsyncTestCase, self).run(result) 

super(AsyncTestCase, self).run(result) не бросает вверх AssertException.

+0

надстройки метод асинхронной '' delay'', что я думаю, что это проблема, по сравнению с тем, от ajdavis – WangST

ответ

1

StackContexts является магическим: Есть много мест в Торнадо, который автоматически перехватывать текущий StackContext и возобновить ее позже. Поэтому, когда delay() звонит done() (предполагается, что delay() реализован в терминах IOLoop.add_timeout или иным образом обрабатывает StackContext правильно), восстановлен ExceptionStackContext.

Аналогичным образом, несмотря на то, что ExceptionStackContext в AsyncTestCase.run() никогда не попадает прямо в какие-либо исключения, он устанавливает «текущий» StackContext для захвата в любом месте теста. (Вот почему это with ExceptionStackContext вместо обычных try/except)

+0

моя '' delay() '' реализована с использованием другого потока, а не '' IOLoop.add_timeout''. В результате, когда исключение происходит в методе обратного вызова '' done() '', исключение не обрабатывается '' StackContext''. поэтому я должен убедиться, что метод обратного вызова '' done() '' будет обрабатываться StackContext, либо '' stack_context.wrap() '' или запустить обратный вызов внутри другого ExceptionStackContext – WangST

+0

Да, если вы используете асинхронные примитивы с нуля, вам придется использовать 'stack_context.wrap', чтобы обеспечить распространение контекста. –

+0

BTW, Поскольку я grep код торнадо, в настоящее время '' StackContext'' в '' stack_context.py'' нигде не используется. Вместо этого используется '' ExceptionStackContext'', и этого достаточно для большинства случаев. это '' StackContext'', разработанный или зарезервированный для некоторых особых случаев? Мне просто интересно. – WangST

0

Если вы внимательно следите за примерами, то ExceptionStackContext работает, как я утверждаю. It's much easier to follow if you read my article.

Вы назвали self.stop с unittest.TestCase, но TestCase не имеет self.stop, только AsyncTestCase. Чтобы использовать TestCase, попробуйте:

import unittest 
import time 
from tornado.stack_context import ExceptionStackContext 
from tornado.ioloop import IOLoop 


def delay(seconds, callback): 
    io_loop = IOLoop.current() 
    io_loop.add_timeout(io_loop.time() + seconds, callback) 


class TestAsync(unittest.TestCase): 
    def test_delay(self): 
     io_loop = IOLoop.current() 
     start = time.time() 

     def handle_exception(typ, val, tb): 
      self.failure = (typ, val, tb) 
      io_loop.stop() 

     def done(): 
      self.assertAlmostEqual(time.time() - start, 2, places=1) 
      io_loop.stop() 

     with ExceptionStackContext(handle_exception): 
      delay(3, done) # fail the assert 
     io_loop.start() 

Примечание я должен был объявить «старт», который вы не в вашем коде, и установить «места = 1». Это работает правильно. Или с AsyncTestCase:

import time 

from tornado import testing 
from tornado.ioloop import IOLoop 


def delay(seconds, callback): 
    io_loop = IOLoop.current() 
    io_loop.add_timeout(io_loop.time() + seconds, callback) 


class TestAsync(testing.AsyncTestCase): 
    def test_delay(self): 
     start = time.time() 
     delay(3, self.stop) 
     self.wait() 
     self.assertAlmostEqual(time.time() - start, 2, places=1) 
+1

повторно запустить эту демку с вашим методом асинхронной '' задержки() '', он работает как спрос. Затем я читаю код '' io_loop.add_timeout'', он использует '' stack_context.wrap'', чтобы обернуть обратный вызов, который, я думаю, является волшебством. Спасибо за ваш комментарий, в следующий раз я должен сначала просмотреть ваш блог. – WangST

Смежные вопросы