Как выполнить модульное тестирование функций записи файлов с помощью python unittest
У меня есть функция Python, которая записывает выходной файл на диск. Я хочу написать unit test для него, используя модуль Uittest для Python. Как я могу утверждать равенство файлов? Я хотел бы получить ошибку, если содержимое файла отличается от ожидаемого + списка различий. Как и на выходе команды unix diff. Есть ли официальный/рекомендуемый способ сделать это?
6 ответов
Самое простое — записать выходной файл, затем прочитать его содержимое, прочитать содержимое золотого (ожидаемого) файла и сравнить его с простым равенством строк. Если они совпадают, удалите выходной файл. Если они разные, поднимите утверждение. Таким образом, когда тесты выполняются, каждый неудачный тест будет представлен выходным файлом, и вы можете использовать сторонний инструмент для их сравнения с золотыми файлами (Beyond Compare отлично подходит для этого). Если вы действительно хотите предоставить свой собственный выход diff, помните, что у stdlib Python есть модуль difflib. Новая поддержка unittest в Python 3.1 включает метод assertMultiLineEqual , который использует его для отображения diffs, аналогично этому:
def assertMultiLineEqual(self, first, second, msg=None): """Assert that two multi-line strings are equal. If they aren't, show a nice diff. """ self.assertTrue(isinstance(first, str), 'First argument is not a string') self.assertTrue(isinstance(second, str), 'Second argument is not a string') if first != second: message = ''.join(difflib.ndiff(first.splitlines(True), second.splitlines(True))) if msg: message += " : " + msg self.fail("Multi-line strings are unequal:\n" + message)
Я предпочитаю, чтобы функции вывода явно принимали дескриптор файла (или файл-подобный объект), вместо того, чтобы принимать имя файла и открывать сам файл. Таким образом, я могу передать объект StringIO в функцию вывода в моем unit test, затем .read() содержимое обратно из этого StringIO (после вызова .seek(0) ) и сравните с моим ожидаемым выходом. Например, мы бы перевели код, подобный этому
##File:lamb.py import sys def write_lamb(outfile_path): with open(outfile_path, 'w') as outfile: outfile.write("Mary had a little lamb.\n") if __name__ == '__main__': write_lamb(sys.argv[1]) ##File test_lamb.py import unittest import tempfile import lamb class LambTests(unittest.TestCase): def test_lamb_output(self): outfile_path = tempfile.mkstemp()[1] try: lamb.write_lamb(outfile_path) contents = open(tempfile_path).read() finally: # NOTE: To retain the tempfile if the test fails, remove # the try-finally clauses os.remove(outfile_path) self.assertEqual(result, "Mary had a little lamb.\n")
##File:lamb.py import sys def write_lamb(outfile): outfile.write("Mary had a little lamb.\n") if __name__ == '__main__': with open(sys.argv[1], 'w') as outfile: write_lamb(outfile) ##File test_lamb.py import unittest from io import StringIO import lamb class LambTests(unittest.TestCase): def test_lamb_output(self): outfile = StringIO() # NOTE: Alternatively, for Python 2.6+, you can use # tempfile.SpooledTemporaryFile, e.g., #outfile = tempfile.SpooledTemporaryFile(10 ** 9) lamb.write_lamb(outfile) outfile.seek(0) content = outfile.read() self.assertEqual(content, "Mary had a little lamb.\n")
Этот подход имеет дополнительное преимущество в том, чтобы сделать вашу функцию вывода более гибкой, если, например, вы решили, что не хотите писать в файл, но какой-то другой буфер, так как он будет принимать все объекты, подобные файлу. Обратите внимание, что использование StringIO предполагает, что содержимое тестового вывода может вписываться в основную память. Для очень большого вывода вы можете использовать временный файл (например, tempfile.SpooledTemporaryFile).
Это лучше, чем записать файл на диск. Если вы выполняете тонны юнит-тестов, IO на диск вызывает всевозможные проблемы, особенно пытаясь их очистить. У меня были тесты записи на диск, tearDown удалял записанные файлы. Тесты будут работать нормально по одному, а затем завершаться с ошибкой при запуске All. По крайней мере, с Visual Studio и PyTools на машине Win. Также скорость.
Хотя это хорошее решение для тестирования отдельных функций, оно по-прежнему проблематично при тестировании реального интерфейса, который предоставляет ваша программа (например, инструмент CLI).