2015-08-24 1 views
1

у меня есть испытательную установку так:Выполнить несколько тестов, в одних и тех же менеджеров контекста

def test1(): 
    with Manager1() as m1, m1.get_m2() as m2: 
     do_test1(m1, m2) 

def test2(): 
    with Manager1() as m1, m1.get_m2() as m2: 
     do_test2(m1, m2) 

, который я бегу с nosetests. Теперь выясняется, что от менеджеров контекста __enter__() и __exit__() методы являются дорогостоящими, поэтому я хотел бы изменить в настройках, как:

def test(): 
    with Manager1() as m1, m1.get_m2() as m2: 
     do_test1(m1, m2) 
     do_test2(m1, m2) 

, но я хотел бы нос по-прежнему сообщать отдельно для каждого теста. Я изучил методы модульного уровня и класса setup() и teardown(), но они, похоже, не дают одинаковых гарантий для очистки в случае исключений. Есть ли чистый способ получить nose для совместной работы с менеджерами контекста?

ответ

1

Если вы беспокоитесь об исключении воспитываются в setup вызывает метод teardown чтобы не называться, тогда вы можете использовать класс ExitStack в contextlib. ExitStack может убедиться, что ваши классы менеджера закрыты должным образом, если случай исключения в setup.

from io import StringIO 
from contextlib import ExitStack 

managers = [] 
close_managers = None 

def setup(): 
    global close_managers 
    with ExitStack() as exit_stack: 
     managers[:] = [exit_stack.enter_context(StringIO()) for _ in range(2)] 
     # any exception raised before this line will will cause all the __exit__ 
     # methods of the given context managers to be called. So this should 
     # be the last part of setup 
     close_managers = exit_stack.pop_all().close 

def teardown(): 
    m1, m2 = managers 
    assert m1.getvalue() == 'test1 m1\ntest2 m1\n' 
    assert m2.getvalue() == 'test1 m2\ntest2 m2\n' 
    close_managers() 

def test1(): 
    m1, m2 = managers 
    m1.write('test1 m1\n') 
    m2.write('test1 m2\n')  

def test2(): 
    m1, m2 = managers 
    m1.write('test2 m1\n') 
    m2.write('test2 m2\n') 
+0

Спасибо, это кажется точным прецедентом для 'ExitStack' (который находится в пакете contextlib2 для python2). Хорошо работает, чтобы гарантировать, что 'm1 .__ exit __()' все еще вызывается, если 'm1.get_m2()' вызывает исключение, сохраняя при этом все функции 'nosetests'. – Stefan

2

Посмотрите на нос test generation. Это может сработать, если изменить код, чтобы использовать выход, что-то вроде:

from nose.tools import nottest 

@nottest 
def do_test1(m1, m2): 
    m1.write('do_test1 f1') 
    m2.write('do_test1 f2') 

@nottest 
def do_test2(m1, m2): 
    m1.write('do_test2 f1') 
    m2.write('do_test2 f2') 


def test(): 
    with open('f1.txt', 'wb') as m1, open('f2.txt', 'wb') as m2: 
     yield do_test1, m1, m2 
     yield do_test2, m1, m2 

Выполнение тестов получает вас:

$ nosetests context.py -v 
context.test(<open file 'f1.txt', mode 'wb' at 0x06A5BF40>, <open file 'f2.txt', mode 'wb' at 0x06A5BF98>) ... ok 
context.test(<open file 'f1.txt', mode 'wb' at 0x06A5BF40>, <open file 'f2.txt', mode 'wb' at 0x06A5BF98>) ... ok 

---------------------------------------------------------------------- 
Ran 2 tests in 0.004s 

OK 
+0

Спасибо, это хорошая идея, но я полагаю, я потеряю возможность выборочно запустить один тест, как: 'nosetests tests.py: test1', который я хотел бы сохранить. Или у вас есть идея, как сохранить это? – Stefan

+0

Кроме того, посмотрите на http://nedbatchelder.com//blog/201508/using_context_managers_in_test_setup.html – Oleksiy

+0

Спасибо за ссылку, но шоу-стоппер приходит в конце: «Остерегайтесь этой техники: менеджер контекстного меню' Метод __exit__' может быть вызван с информацией об осуществляемом исключении. Механизм, показанный здесь, никогда этого не сделает ». Кажется, что «ExitStack» - это просто способ использования менеджеров контекста с правильной обработкой исключений, если некоторая библиотека, такая как «нос», мешает нам использовать блок 'with'. – Stefan