2015-08-11 2 views
0

Я запускаю unittests в обратных вызовах для вызовов базы данных двигателя, и я успешно поймаю AssertionErrors и получаю их поверхность при запуске nosetests, но AssertionErrors попадают в неправильный тест. Отслеживаемые файлы относятся к разным файлам.Тестирование вызовов модемов с помощью IOLoop

Мои UnitTests выглядят обычно так:

def test_create(self): 
    @self.callback 
    def create_callback(result, error): 
     self.assertIs(error, None) 
     self.assertIsNot(result, None) 
    question_db.create(QUESTION, create_callback) 
    self.wait() 

А класс unittest.TestCase Я использую выглядит следующим образом:

class MotorTest(unittest.TestCase): 
    bucket = Queue.Queue() 
    # Ensure IOLoop stops to prevent blocking tests 
    def callback(self, func): 
     def wrapper(*args, **kwargs): 
      try: 
       func(*args, **kwargs) 
      except Exception as e: 
       self.bucket.put(traceback.format_exc()) 
      IOLoop.current().stop() 
     return wrapper 

    def wait(self): 
     IOLoop.current().start() 
     try: 
      raise AssertionError(self.bucket.get(block = False)) 
     except Queue.Empty: 
      pass 

Ошибки я вижу:

====================================================================== 
FAIL: test_sync_user (app.tests.db.test_user_db.UserDBTest) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/Users/----/Documents/app/app-Server/app/tests/db/test_user_db.py", line 39, in test_sync_user 
    self.wait() 
    File "/Users/----/Documents/app/app-Server/app/tests/testutils/mongo.py", line 25, in wait 
    raise AssertionError(self.bucket.get(block = False)) 
AssertionError: Traceback (most recent call last): 
    File "/Users/----/Documents/app/app-Server/app/tests/testutils/mongo.py", line 16, in wrapper 
    func(*args, **kwargs) 
    File "/Users/----/Documents/app/app-Server/app/tests/db/test_question_db.py", line 32, in update_callback 
    self.assertEqual(result["question"], "updated question?") 
TypeError: 'NoneType' object has no attribute '__getitem__' 

Где ошибка, как сообщается, находится в UserDbTest, но явно находится в test_questions_db.py (что является QuestionDbTest)

У меня возникли проблемы с носетестами и асинхронными тестами в целом, поэтому, если у кого-то есть какие-либо советы по этому поводу, это было бы также весьма полезно.

ответ

1

Я не могу полностью понять ваш код без SSCCE, но я бы сказал, что вы принимаете неразумный подход к асинхронному тестированию в целом.

Особая проблема, с которой вы сталкиваетесь, заключается в том, что вы не дожидаетесь завершения теста (асинхронно) перед тем, как покинуть тестовую функцию, так что в IOLoop работа продолжается, когда вы возобновите цикл в следующем тесте. Используйте собственный модуль тестирования Tornado - он предоставляет удобные методы для запуска и остановки цикла, и он воссоздает цикл между тестами, поэтому вы не испытываете помех, как то, что вы сообщаете. Наконец, он имеет чрезвычайно удобное средство тестирования сопрограмм.

. Например:

import unittest 
from tornado.testing import AsyncTestCase, gen_test 

import motor 

# AsyncTestCase creates a new loop for each test, avoiding interference 
# between tests. 
class Test(AsyncTestCase): 
    def callback(self, result, error): 
     # Translate from Motor callbacks' (result, error) convention to the 
     # single arg expected by "stop". 
     self.stop((result, error)) 

    def test_with_a_callback(self): 
     client = motor.MotorClient() 
     collection = client.test.collection 
     collection.remove(callback=self.callback) 

     # AsyncTestCase starts the loop, runs until "remove" calls "stop". 
     self.wait() 

     collection.insert({'_id': 123}, callback=self.callback) 

     # Arguments passed to self.stop appear as return value of "self.wait". 
     _id, error = self.wait() 
     self.assertIsNone(error) 
     self.assertEqual(123, _id) 

     collection.count(callback=self.callback) 
     cnt, error = self.wait() 
     self.assertIsNone(error) 
     self.assertEqual(1, cnt) 

    @gen_test 
    def test_with_a_coroutine(self): 
     client = motor.MotorClient() 
     collection = client.test.collection 
     yield collection.remove() 
     _id = yield collection.insert({'_id': 123}) 
     self.assertEqual(123, _id) 
     cnt = yield collection.count() 
     self.assertEqual(1, cnt) 

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

(В этом примере я создаю новый MotorClient для каждого теста, который является хорошей идеей при тестировании приложений, использующих двигатель Вашего фактическое применение не должен создать новую MotorClient для каждой операции. для достойной производительности необходимо создать один MotorClient, когда начинается ваше приложение, и использовать тот же один клиент на протяжении всей жизни процесса.)

Посмотрите на модуле тестирования, и в частности gen_tes т декоратор:

http://tornado.readthedocs.org/en/latest/testing.html

Эти тестовые удобства заботиться о многих деталях, связанных с модульное тестирование приложений Tornado.

Я прочитал лекцию и написал статью о тестировании в Торнадо есть подробнее здесь:

http://emptysqua.re/blog/eventually-correct-links/

+0

Awesome, спасибо, я буду смотреть на них и доложить – sihrc

+0

Великого. Я добавил примечание о воссоздании MotorClient в тестах, но с использованием одного долгоживущего клиента в реальном приложении. –

+0

Правильно! Когда я использовал один и тот же MotorClient и запускал издевавшиеся приложения в потоках, он жаловался на переключение контекстов по потокам - так как клиенты должны жить и получать доступ к одному и тому же потоку вправо? – sihrc

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