Как подавить ResourceWarning()в doctest, запущенном в unittest

#python #python-unittest #suppress-warnings #doctest

Вопрос:

Мои тесты на Python открывают некоторые файлы, которые он никогда не закрывает. Это не вызывает никаких проблем; они автоматически закрываются при уничтожении объекта, и добавление логики для обеспечения их явного закрытия излишне усложнило бы мою документацию.

Однако, когда тесты выполняются внутри unittest , они начинают выдавать ResourceWarning() s, добавляя бесполезный шум к моим выводам.

Например, учитывая leak.py:

 def hello(f):
    """ 
    >>> a = open("test-file","w")
    >>> hello(a)
    >>> open("test-file").read()
    'hello'
    """
    f.write("hello")
    f.flush()

def load_tests(loader, tests, ignore):
    import doctest
    tests.addTests(doctest.DocTestSuite())
    return tests
 

Запуск его под управлением doctest и unittest с Python 3.6.9 генерирует:

 $ python3 --version
Python 3.6.9
$ python3 -m doctest leak.py -v
[...] 
3 passed and 0 failed.
Test passed.
$ python3 -m unittest leak
/tmp/fileleak/leak.py:1: ResourceWarning: unclosed file <_io.TextIOWrapper name='test-file' mode='r' encoding='UTF-8'>
  def hello(f):
/usr/lib/python3.6/doctest.py:2175: ResourceWarning: unclosed file <_io.TextIOWrapper name='test-file' mode='w' encoding='UTF-8'>
  test.globs.clear()
.
----------------------------------------------------------------------
Ran 1 test in 0.003s

OK
 

Есть несколько способов очистить это внутри доктестов, но все они добавляют сложности, которые отвлекали бы от документации. Это включает в себя явные вызовы a.close() , использование with open("test-file") as a: (которое также помещает тестируемый вывод ниже with блока) или прямое удаление предупреждений с помощью » warnings.simplefilter(«игнорировать»).

Как я могу запустить тесты, unittest чтобы подавить ResourceWarning() s, как doctest это делает?

Ответ №1:

doctest не подавляет эти предупреждения. unittest это дает им возможность. Мы, вероятно, хотим unittest включить их для наших более традиционных тестов, поэтому мы не хотим подавлять их глобально.

Я уже использую load_tests , чтобы добавить unittest тесты, так что у нас есть хорошее место для этого. Мы не можем просто позвонить warnings.filterwarnings() напрямую load_tests , так как фильтры сбрасываются перед запуском нашего теста. Мы можем использовать setUp аргумент doctest.DocTestSuite , чтобы предоставить функцию, которая выполнит эту работу за нас.

 def load_tests(loader, tests, ignore):
    import doctest
    import warnings
    def setup(doc_test_obj):
        for module in (__name__, 'doctest'):
            warnings.filterwarnings("ignore",
                    message= r'unclosed file <_io.TextIOWrapper',
                    category=ResourceWarning,
                    module=module "$")
    tests.addTests(doctest.DocTestSuite(setUp=setup))
    return tests
 

Нам нужны фильтры в любом месте, где созданный нами объект может быть уничтожен и
ResourceWarning сгенерирован. Это включает в себя наш собственный модуль ( __name__ ), но он
также включает doctest в себя, поскольку некоторые глобальные объекты не уничтожаются до тех пор, пока
DocTestCase.tearDown .

Тщательно указав, что фильтровать по категориям, сообщениям и модулям, это должно ограничить риск подавления разработанных предупреждений, но это не безрисково.