python -m unittest discover /home/kortide "Atest.py"
上面是执行我放在桌面的Atest.py测试模块。
这里的-m什么意思呢?python -h说明如下:
-m mod : run library module as a script (terminates option list)
把库模块作为一个脚本来运行,当把unittest作为一个脚本来运行的时候,就会运行该文件夹下的__init__和__main__。
import sys
print sys.argv[0]
if sys.argv[0].endswith("__main__.py"):
sys.argv[0] = "python -m unittest"
__unittest = True
from .main import main, TestProgram, USAGE_AS_MAIN
TestProgram.USAGE = USAGE_AS_MAIN
main(module=None)
运行到__main__.py时,打印sys.argv[0],/usr/lib/python2.7/unittest/__main__.py,正好是当前执行文件__main__.py的所在路径。(和C、C++一样)
最后同样会去调用main,并设置这时候的module=None。
def parseArgs(self, argv):
if len(argv) > 1 and argv[1].lower() == 'discover':
self._do_discovery(argv[2:])
return
def _do_discovery(self, argv, Loader=None):
if Loader is None:
Loader = lambda: self.testLoader
# handle command line args for test discovery
self.progName = '%s discover' % self.progName
import optparse
parser = optparse.OptionParser()
parser.prog = self.progName
parser.add_option('-v', '--verbose', dest='verbose', default=False,
help='Verbose output', action='store_true')
if self.failfast != False:
parser.add_option('-f', '--failfast', dest='failfast', default=False,
help='Stop on first fail or error',
action='store_true')
if self.catchbreak != False:
parser.add_option('-c', '--catch', dest='catchbreak', default=False,
help='Catch ctrl-C and display results so far',
action='store_true')
if self.buffer != False:
parser.add_option('-b', '--buffer', dest='buffer', default=False,
help='Buffer stdout and stderr during tests',
action='store_true')
parser.add_option('-s', '--start-directory', dest='start', default='.',
help="Directory to start discovery ('.' default)")
parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
help="Pattern to match tests ('test*.py' default)")
parser.add_option('-t', '--top-level-directory', dest='top', default=None,
help='Top level directory of project (defaults to start directory)')
options, args = parser.parse_args(argv)
if len(args) > 3:
self.usageExit()
for name, value in zip(('start', 'pattern', 'top'), args):
setattr(options, name, value)
# only set options from the parsing here
# if they weren't set explicitly in the constructor
if self.failfast is None:
self.failfast = options.failfast
if self.catchbreak is None:
self.catchbreak = options.catchbreak
if self.buffer is None:
self.buffer = options.buffer
if options.verbose:
self.verbosity = 2
start_dir = options.start
pattern = options.pattern
top_level_dir = options.top
loader = Loader()
self.test = loader.discover(start_dir, pattern, top_level_dir)
在_do_discovery中会导入optparse模块,在其中添加有效的参数。然后根据设定的有效参数去分析传入的参数,options, args = parser.parse_args(argv)。
然后再在for循环中设置分析出来的属性值:
for name, value in zip(('start', 'pattern', 'top'), args):
setattr(options, name, value)
最后再通过Loader根据设置好的属性去获取TestSuite类的一个对象。
loader.py -- def discover(self, start_dir, pattern='test*.py', top_level_dir=None)
set_implicit_top = False
if top_level_dir is None and self._top_level_dir is not None:
# make top_level_dir optional if called from load_tests in a package
top_level_dir = self._top_level_dir
elif top_level_dir is None:
set_implicit_top = True
top_level_dir = start_dir
top_level_dir = os.path.abspath(top_level_dir)
获取将要提取的TestSuite所在的顶层目录。然后把顶层目录加到sys.path中。
is_not_importable = False
if os.path.isdir(os.path.abspath(start_dir)):
start_dir = os.path.abspath(start_dir)
if start_dir != top_level_dir:
is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py'))
判断传入的参数是否是一个目录文件,如果是,就获取start_dir的绝对路径和top_level_dir进行比较,如果不是top_level_dir,那么就判断该目录是否作为一个包,
判断依据就是是否存在__init__.py文件。举个例子说明下,在当前目录current下,存在One目录, One中存在很多*test.py,Two中存在subTwo目录,
根据上面的说明,如果你的top_level_dir = current,start_dir = One,那么One中就需要存在__init__.py,也就是把One做成一个包。否则后面会一起一个异常:
if is_not_importable:
raise ImportError('Start directory is not importable: %r' % start_dir)
最后在正确的start_dir下,搜索需要的测试用例列表,转换成TestSuite类型后返回。
tests = list(self._find_tests(start_dir, pattern))
return self.suiteClass(tests)