Модульное тестирования на Python на примере копирования файлов
На днях встала задача написать скрипт копирующий новые файлы из одной директории в другую и пропускать уже скопированные. Если возникают какие-либо ошибки, то все они записываются в лог. Хочу обратить ваше внимание, что все тестовые методы изолированны друг от друга, так что если сломается один метод, то другие продолжат выполняться. А так же методы setUp и tearDown подготавливают среду для выполнения тестов.
'''
Created on 08.12.2010
@author: Razzhivn A.S.
@site: httpbots.com
'''
import unittest
from file_utils import CopyFilesUtil
import os
from shutil import rmtree
class LogCopiedFilesTest(unittest.TestCase):
dst_dir = os.path.abspath('dst_dir')
src_dir = os.path.abspath('src_dir')
log = os.path.abspath('log.txt')
cpFileName = 'file_1'
def setUp(self):
os.mkdir(self.dst_dir)
os.mkdir(self.src_dir)
self.logcpfiles = CopyFilesUtil(self.src_dir, self.dst_dir, self.log)
self.createTestSrcFiles()
def tearDown(self):
os.remove(self.log)
rmtree(self.dst_dir)
rmtree(self.src_dir)
def testCopyUnlogedFiles(self):
self.logcpfiles.write_copylog(self.cpFileName)
self.logcpfiles.copyUnlogedFiles()
assert not self.cpFileName in os.listdir(self.dst_dir)
def testCopy(self):
self.logcpfiles.copy(self.cpFileName)
assert os.path.exists(os.path.join(self.dst_dir, self.cpFileName))
def testReadWriteCopyLog(self):
self.logcpfiles.write_copylog(self.cpFileName)
filenames = self.logcpfiles.read_copylog()
assert self.cpFileName in filenames
def testDstDirUnexists(self):
unexist_dir = 'Z:\\'
self.logcpfiles = CopyFilesUtil(self.src_dir, unexist_dir, self.log)
self.logcpfiles.copyUnlogedFiles()
assert not self.logcpfiles.read_copylog()
assert os.path.exists(self.logcpfiles.error_log)
os.remove(self.logcpfiles.error_log)
def createTestSrcFiles(self):
for i in range(0,4):
open(os.path.join(self.src_dir, '%s%s'%(self.cpFileName[:-1], i)), 'w').close()
if __name__ == "__main__":
#import sys;sys.argv = ['', 'Test.testName']
unittest.main()
А вот и сам файл file_utils.py:
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
Created on 08.12.2010
@author: Razzhivin A. S.
@site: httpbots.com
'''
import os
from shutil import copyfile
from time import localtime, strftime
def writeErrorLog(error_log, msg):
'''Записать лог ошибок
>>> writeErrorLog('error.log', 'error message')
>>> f = open('error.log', 'r')
>>> 'error message' in f.readline()
True
>>> f.close()
>>> os.remove('error.log')
'''
if not os.path.exists(error_log):
open(error_log, 'w').close()
t = strftime("%d/%m/%Y %H:%M:%S", localtime())
f = open(error_log, 'a')
f.write('[%s] %s' % (t, msg))
f.close()
class CopyFilesUtil:
error_log = os.path.abspath('cp_error.log')
def __init__(self, src_dir, dst_dir, copylog):
'''@param src_dir: откуда
@param dst_dir: куда
@param copylog: лог файлов которые уже были скопированы'''
self.src_dir = src_dir
self.dst_dir = dst_dir
self.copylog = copylog
# если лог файла нет, то надо его создать
if not os.path.exists(self.copylog):
open(self.copylog, 'w').close()
def copyUnlogedFiles(self):
'''Скопировать файлы, которых нет в логе'''
try:
copied_files = self.read_copylog()
for f in os.listdir(self.src_dir):
if not (f in copied_files):
self.copy(f)
self.write_copylog(f)
except OSError as e:
writeErrorLog(self.error_log, "File doesn't exists. "+e.__str__()+"\n")
except Exception as e:
writeErrorLog(self.error_log, e.__str__())
def read_copylog(self):
f = open(self.copylog, 'r')
filenames = [line.rstrip('\n') for line in f.readlines()]
f.close()
return filenames
def write_copylog(self, filename):
f = open(self.copylog, 'a')
f.write(filename+'\n')
f.close()
def copy(self, file):
'''Копирует файл из src_dir в dst_dir. dst_dir создаётся в случае отсутсвия
@param file: копируемый файл'''
src_fpath = os.path.join(self.src_dir, file)
dst_fpath = os.path.join(self.dst_dir, file)
if not os.path.exists(self.dst_dir):
os.makedirs(self.dst_dir)
copyfile(src_fpath, dst_fpath)
def main():
try:
lgcpf = CopyFilesUtil('E:\\tmp\\CTC', 'Z:\\', 'E:\\tmp\\copy_log.txt')
lgcpf.copyUnlogedFiles()
except Exception as e:
writeErrorLog(CopyFilesUtil.error_log, e.__str__())
if __name__ == '__main__':
import doctest
doctest.testmod()
main()
Ещё пару слов про магические методы setUp и tearDown. Эти методы запускаются перед выполнением каждого тестового метода. Поэтому эти методы служат для подготовки среды выполнения методов. И ещё, последовательность вызова тестовых методов случайная. Для простых функций утилит вполне подходит doctest, поэтому я протестил метод writeErrorLog с его помощью.
Думаю, что получившиеся тесты довольно информативны. Разобраться в том, как работает не составит труда. p.s. под linux-ом один тест не пройдёт=) В качестве упражнения можете разобраться почему))

