TestLoader源码解析
def loadTestsFromTestCase(self, testCaseClass) #看名称分析:从TestCase找测试集--那么就是把我们的def用例加载到testSuit里面
def loadTestsFromModule(self, module, *args, pattern=None, **kws) #看名称分析:从模块里面找测试集,那么 模块>类>test_方法>添加到testSuit里面
def loadTestsFromName(self, name, module=None) #看名称分析: 接收到name直接添加到testSuit里面
def loadTestsFromNames(self, names, module=None) #看名称分析:接受到的是一个包含测试test_方法的列表
def getTestCaseNames(self, testCaseClass) # 看名称分析: 取出一个包含test_方法的列表
def discover(self, start_dir, pattern='test*.py', top_level_dir=None) ## 看名称分析:发现--找test_方法
def _get_directory_containing_module(self, module_name) #获取目录包含的模块
def _get_name_from_path(self, path) #从路径从找名称
def _get_module_from_name(self, name) #从名称找模块
def _match_path(self, path, full_path, pattern) #正则匹配路径--参数包含pattern 那估计是匹配我们测试脚本格式的
def _find_tests(self, start_dir, pattern, namespace=False) #找测试集合
def _find_test_path(self, full_path, pattern, namespace=False) #找测试集合的路径
那就是1234
一个discover,getTest,_match_path
二个find
三个_get
四个loadTests discover 逻辑
>
_find_tests【两个处理逻辑 一个是本次传的目录和上次传的一样或不一样,】
【一样:直接从我们传的目录下面继续去找testcaose---】
【不一样:会从我们传的目录下面去执行os.path.listdir找到所有的子文件列表paths(文件)),然后遍历得到单独的path做 start_dir+path拼接】
>
①—get_name_from_path【传入start_dir,判断当前传入的目录是否为上次传入的顶级目录返回".",不一样可能有点绕-并返回一个值这个值有四种情况 . test ...test dir.tests--正常应该是返回test文件名
②_find_test_path(self, full_path, pattern, namespace=False)
【执行这个从路径中找test,那么很明显 一样:传目录路径 不一样传文件路径 】
_find_test_path
class TestLoader(object):
"""
This class is responsible for loading tests according to various criteria
and returning them wrapped in a TestSuite
"""
testMethodPrefix = 'test'
sortTestMethodsUsing = staticmethod(util.three_way_cmp)
suiteClass = suite.TestSuite
_top_level_dir = None def __init__(self):
super(TestLoader, self).__init__()
self.errors = []
# Tracks packages which we have called into via load_tests, to
# avoid infinite re-entrancy.
self._loading_packages = set() #这里创建了一个空的self._loading_packages={}无序且不重复的元素集合
def discover(self, start_dir, pattern='test*.py', top_level_dir=None): #一般我们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
#上面这一串花里胡哨的东西就是处理顶层目录-如果是第一次启动服务-
#就走elif-top_level_dir==我们下面传的值--之后--self._top_level_dir就不为空了,
#但是top_level_dir 顶部是处理==None所以会走if=True
top_level_dir = os.path.abspath(top_level_dir)#转绝对路径
if not top_level_dir in sys.path:
#这里是防止重复将top_level_dir加入执行目录--BUT如果我第一次传的start_dir=a,第二次传的start_dir=b
#分析一下--第一次就是把a加入到了执行目录---下面self._top_level_dir=a 二次(复次)传b的时候,会出现top_level_dir=a---并没有判断b是否在执行目录
#这里加一波问号?????????????????????
#但是一般情况 我们目录就只有一个--所以这里--先放着。。。先看后面再来看这里
# all test modules must be importable from the top level directory
# should we *unconditionally* put the start directory in first
# in sys.path to minimise likelihood of conflicts between installed
# modules and development versions?
sys.path.insert(0, top_level_dir)
self._top_level_dir = top_level_dir
#如果top_level_dir我们传的目录不在可执行目录--则临时添加进去
is_not_importable = False #是否 不能导入
is_namespace = False #is_namespace那么这个字段的意思就是是否可以找到传入的路径
tests = []
if os.path.isdir(os.path.abspath(start_dir)): #判断我们传的是否为一个目录--实际上这里直接用top_level_dir不香吗--
start_dir = os.path.abspath(start_dir)
# 之前把top_level_dir = start_dir
# 然后top_level_dir = os.path.abspath(top_level_dir)
#现在start_dir = os.path.abspath(start_dir)
#为什么不 直接用top_level_dir? 小朋友你是否有许多问号
# 问题出在上面--复次的时候--并没有走 top_level_dir = start_dir 而是走的 top_level_dir = self._top_level_dir ,
#所以如果我们上次传的路径如果和这次不一样--那么top_level_dir是不等于start_dir--而start_dir才是我们传的--
if start_dir != top_level_dir: #所以这里相当于判断前后传的路径是否一样--一般来说我们的start_dir都是等于top_level_dir的
is_not_importable = not os.path.isfile(os.path.join(start_dir, '__init__.py'))#如果是一个文件返回false
#如果不一样--则判断我们当前传入的start_dir/__init__.py是不是一个正确的文件路径..os.path.isfile() 返回布尔值
else: #如果我们传入的不是一个目录,就开始一堆花里胡哨的报错东西了。。暂时不用管
# support for discovery from dotted module names
try:
__import__(start_dir)
#这里就很有意思了---__impor__("PyFiles.Besettest")那就是导入PyFiles
#那么也就是说这个97.33的概率会报错--也就是说你如果目录错了--下面的else基本不会走。。。除非你很神奇的填的路径右侧是一个可导入的模块
except ImportError:
is_not_importable = True #如果导入不鸟--就is_not_importable设置为true 在这里我清楚了这个字段的含义--不能导入=true
else:#那么这里假设导入成功之后
the_module = sys.modules[start_dir] #这里是如果我们导入成功--就走这里-取出start_dir导入的赋值给the_module
top_part = start_dir.split('.')[0] #这里是将我们导入的模块名称取出来
try:
start_dir = os.path.abspath( #打印导入模块所在目录的绝对路径
os.path.dirname((the_module.__file__)))
except AttributeError: #这里是如果导入模块成功了---但是尼玛打印导入模块的绝对路径又报错--不想看了+2
# look for namespace packages
try: #然后有开始进行模块导入检查---日了狗了。。。。。
# fuck----想直接关机了+1,这里估计是想找到为什么不能导入的原因。。大神的思路就是完美,如果是我就抛出一个目录不对就完事
#这一块的学习 文档 Python标准模块--import
spec = the_module.__spec__
#将导入成功的模块的规格说明赋值给spec--
#打印出来就是ModuleSpec(name='besettest.interface', loader=<_frozen_importlib_external.SourceFileLoader object at 0x0000000003D6E780>, origin='E:\\PyFiles\\Besettest\\besettest\\interface\\__init__.py', submodule_search_locations=['E:\\PyFiles\\Besettest\\besettest\\interface'])
#这么一串东西--也没用过。。。。大概就是模块名称、路径、导入的模块对象吧
#origin 加载模块的位置--
#loader_state模块特定数据的容器
except AttributeError: #如果模块的规格说明取不出来。。。。。。。
spec = None #我查阅了一下。。。的确存在部分模块规格说明为None的--所以还得继续往下看 if spec and spec.loader is None: #如果存在规格说明 且 数据容器为None。
if spec.submodule_search_locations is not None:
#这是个什么玩意呢--模块 搜索 位置s(列表)。。。
is_namespace = True #如果spec.submodule_search_locations不为none -----
# 2.5级英文翻译 就是模块的路径 如果模块路径不为空is_namespace可以找到---is_namespace那么这个字段的意思就是存在命名空间。。就是可以找到这个模块
for path in the_module.__path__: #这里我研究怀疑是故意提升逼格。。the_module.__path__==spec.submodule_search_locations
if (not set_implicit_top and #首次set_implicit_top==True 复次set_implicit_top==Fase
not path.startswith(top_level_dir)):
continue
#这里让我稍微有点疑惑。。为什么要判断是首次还是复次--我猜是判断the_module.__path__列表里面有几个某块的路径
#如果是首次直接下一步--如果是复次会有多个路径。但是如果是复次top_level_dir这个路径又是上次的。。。日
#他的作用是找到导入模块的路径-知道这个就行。。。
self._top_level_dir = \
(path.split(the_module.__name__
.replace(".", os.path.sep))[0]) #取出导入模块的上级目录绝对路径。。。
#the_module.__name__.replace(".", os.path.sep) 这一串我看来是没有必要的。。因为 the_module.__name__既然取到了模块名称那他肯定是一个字符串
tests.extend(self._find_tests(path, #然后调用_find_tests 寻找测试。加入tests列表--这个有点熟悉的味道---
pattern, #我觉得基本不会走这里去找---脚本路径一般都会填对 填错了,都不知道执行到哪里去了。。。
namespace=True))
elif the_module.__name__ in sys.builtin_module_names:
#判断 sys.builtin_module_names返回一个列表,包含所有已经编译到Python解释器里的模块的名字 和sys.models是一个字典
#就是没法导入报错
# builtin module
raise TypeError('Can not use builtin modules '
'as dotted module names') from None
else: #没发现这个模块
raise TypeError(
'don\'t know how to discover from {!r}'
.format(the_module)) from None if set_implicit_top: #如果是首次。。。。
if not is_namespace: #is_namespace默认的是false-且可以找到模块相关规格
self._top_level_dir = \
self._get_directory_containing_module(top_part) #interface.testFiles interface假设这个是导入的 -self._top_level_dir 是一个目录的绝对路径
#top_part导入的模块名称-----
sys.path.remove(top_level_dir) #只知道是从系统路径移除--但是不知道为什么移除。。。。
else:
sys.path.remove(top_level_dir) # if is_not_importable: #如果我们传的文件不能导入---就直接抛出异常
raise ImportError('Start directory is not importable: %r' % start_dir) if not is_namespace: #is_namespace默认的是false--这里就是可以找到模块。。。。
tests = list(self._find_tests(start_dir, pattern))
return self.suiteClass(tests)
def _find_tests(self, start_dir, pattern, namespace=False): #注意这里如果我们传的不是脚本目录而是一个可导入的模块namespace是等于True的
"""Used by discovery. Yields test suites it loads."""
# Handle the __init__ in this package
name = self._get_name_from_path(start_dir) #返回一个name name存在三种返回情况 "."-当本次和上次传入的start_dir一致 不一致 "文件名" "...文件名"
#get_name_from_path的逻辑在这里就很清晰了 # name is '.' when start_dir == top_level_dir (and top_level_dir is by
# definition not a package).
if name != '.' and name not in self._loading_packages:
#当name最少有一个且也不再self._loading_packages.【self._loading_packages初始化的时候建的空集合】 走下面这个
# name is in self._loading_packages while we have called into
# loadTestsFromModule with name.
tests, should_recurse = self._find_test_path( #然后这里start_dir是我们传的模块--他就去找。。这里就恢复到了传测试目录的逻辑了
start_dir, pattern, namespace)
if tests is not None:
yield tests
if not should_recurse:
# Either an error occurred, or load_tests was used by the
# package.
return
# Handle the contents.
paths = sorted(os.listdir(start_dir)) #那就从这里开始--当我们穿的目录和上次一样-他会找到目录下所有的文件然后排序--我们的用例执行顺序就是从这里开始搞了。。
for path in paths: #遍历我们传的目录下的所有文件
full_path = os.path.join(start_dir, path) 将我们传入的目录和目录下的py文件拼接的完整路径
tests, should_recurse = self._find_test_path( #把我们文件路径和我们的文件格式传入_find_test_path这个方法--
full_path, pattern, namespace)
如果当前传的是一个目录-会返回should_recurse=True--这个英文直译是应该_递归--下面yield from 就是执行递归的操作
if tests is not None:
yield tests
if should_recurse: #这句是判断他是不是一个目录
# we found a package that didn't use load_tests.
name = self._get_name_from_path(full_path)
self._loading_packages.add(name)
try:
yield from self._find_tests(full_path, pattern, namespace)
finally:
self._loading_packages.discard(name)
def a(n):
testList=b(n)
return testList def b(n,m=1):
print("执行第%s次"%m)
for a in range(n):
if not divmod(a,2)[1] and a!=0:
print(a)
yield a #是用yield之后返回的是一个生成器
if divmod(a,3)[1]:
m =m+1
yield from b(a,m) #重新执行b方法 print(list(a(7))) 执行第1次
2
执行第2次
4
执行第3次
2
执行第4次
6
[2, 4, 2, 6]
name = self._get_name_from_path(start_dir) #因为discover我们是支持我们传目录或者模块寻找testcase的,所以这个方法 def _get_name_from_path(self, path):
#主要正确逻辑三个 比如我们的脚本目录结构是 E://a/b/ b目录下面有script.py 和 /c/script.py
#第一次是我们自己传的目录--之前在discover他做了一个处理 就是第一次运行时会把我们传的目录赋值给顶层目录---
#第一个逻辑判断我们传的是不是 -和顶层一样---一样的话===_find_tests方法就直接从目录下面找脚本-当如如果有目录也会继续走--他是在_find_tests_path判断的--最终也是回到找脚本模块上
#如果不一样--那就是找了 找到这个目录了---那么就从顶层开始找这个目录的相对路径--其实就是找最后那个目录(必须是一个packge。上面说的目录都是包)、。。然后返回一个name
#如果还有子目录 d---那就会返回 c.d
if path == self._top_level_dir: #首次运行pattern,top_level_dir=None):self._top_level_dir = top_level_dir = start_dir -第二次运行如果目录没有变,这里也是直接返回的
return '.' path = _jython_aware_splitext(os.path.normpath(path)) #如果我们当前传的和上次传的目录不一致。。这里得path我们当前传的路径 _relpath = os.path.relpath(path, self._top_level_dir) #从self._top_level_dir开始找path的相对路径
#这里是从我们传的path开始找到self._top_level_dir上次传的相对路径
#例如: path=path1="E:\\PyFiles\\Besettest\\besettest\\interface\\testFiles" self._top_level_dir="E:\\PyFiles\\Besettest\\besettest\\interface\\result"
#那么_relpath="..\testFiles" -暂时还不清楚为什么要找这个??????????????????????????
assert not os.path.isabs(_relpath), "Path must be within the project"
#↑↑断言 不是绝对路径-也就是说_relpath是否为相对路径↑↑↑特么的 这里肯定是一个相对路径啊。。。上面都有relpath了。。。丢
#↓↓↓↓断言以..开头就失败。。。↓↓↓--这两处超出理解范围了。。。。。。
assert not _relpath.startswith('..'), "Path must be within the project" name = _relpath.replace(os.path.sep, '.') #然后这里又把分隔符替换成. 返回 a.b 当然或许会有异常情况返回.....这是我意淫的
return name
def _find_test_path(self, full_path, pattern, namespace=False):
#_find_tests()调用这个方法 传了一个我们传的目录下的a文件路径、和需要找的文件pattern-namespace【传的可能是true 也可能是false】,如果我的目录是对的-namespace传的就是false
"""Used by discovery. Loads tests from a single file, or a directories' __init__.py when
passed the directory. Returns a tuple (None_or_tests_from_file, should_recurse).
"""
basename = os.path.basename(full_path) #basename==文件名.py后续带py的统称文件--不带后缀的统称文件名。。。
if os.path.isfile(full_path): #如果我们传的full_path是一个文件---我们在discover传的是一个脚本目录-之前在_test_find是做了一个拼接得到的完整路径full_path
if not VALID_MODULE_NAME.match(basename): #判断他是不是一个py文件---- # valid Python identifiers only
return None, False #如果不是直接返回
if not self._match_path(basename, full_path, pattern): #这里虽然传了三个值--但是实际上只有basename,pattern有用--
#_match_path调用fnmatch(文件, 我们传的文件格式或文件)这个需要————from fnmatch import fnmatch他的主要作用是做此模块的主要作用是文件名称的匹配
#当此次传入的文件名与我们的文件格式匹配一致self._match_path返回true
return None, False #如果不一样 就直接回到 _find_test继续找
# if the test file matches, load it
name = self._get_name_from_path(full_path) #然后这里把文件路径又传到 self._get_name_from_path去返回文件名-这个时候因为我们传的是脚本目录-full_path目录下的文件路径,所以返回的name 就是文件名 #self._top_level_dir是当前文件目录路径,path是当前文件路径--从目录找文件--直接就是文件名--他返回的name就是文件名
try:
module = self._get_module_from_name(name) #_get_module_from_name 这个方法就是动态导入模块名--然后返回一个所有导入的模块的对象 moudel.__file__路径、moudel.__name__名称
except case.SkipTest as e: #如果导入不成功 case.SkipTest 实际上case是继承--Exception--所以把这个理解为Exception就可以了--
return _make_skipped_test(name, e, self.suiteClass), False
except:
error_case, error_message = \
_make_failed_import_test(name, self.suiteClass)
self.errors.append(error_message)
return error_case, False
else: #module 获取到值之后走这里。。
mod_file = os.path.abspath(
getattr(module, '__file__', full_path)) #然后这里取出我们导入模块的 绝对路径---如果反射找不到就返回该文件的路径-其实差别不大-处理一下更严谨
realpath = _jython_aware_splitext(
os.path.realpath(mod_file)) #os.path.realpath(mod_file)然后又返回真实路径---然后又去掉路径的.py,。,,,,,,,,丢
fullpath_noext = _jython_aware_splitext(
os.path.realpath(full_path)) #然后full_path 找真实路径去掉.py
if realpath.lower() != fullpath_noext.lower(): #如果动态导入的模块的目录路径 不等于 传进来(也就是pattern)的目录路径--实际上传进来的路径肯定是个绝对路径--因为前面已经转了好几次绝对路径了
module_dir = os.path.dirname(realpath) #不等于就找动态导入模块所在的目录----实际上上面处理的realpath已经是一个目录了。。但是他防止realpath还是一个.py文件。所以又操作了一次
mod_name = _jython_aware_splitext( #full_path是文件的路径.py的,然后这里又先是basename取出文件(就是把路径去掉,只留下xxx.py) 然后外面那个方法 把.py去掉--留下文件名
os.path.basename(full_path))
expected_dir = os.path.dirname(full_path)
#然后找到需要执行脚本所在的目录。。。。。也就是说正常情况 假设expected_dir="e://a/b" 那么 mod_file =realpathfullpath_noext="e://a/b/scripy"
#scripy是一个py文件---上面这个if是说的 正常情况。。。我想不到导入模块和导入模块的路径不相等的情况--不过这个不重要-源码这样肯定是有道理的
msg = ("%r module incorrectly imported from %r. Expected "
"%r. Is this module globally installed?")
raise ImportError(
msg % (mod_name, module_dir, expected_dir))
return self.loadTestsFromModule(module, pattern=pattern), False
#然后走 从模块从加载测试s 这个方法---也就是说discover实际上是调用loadTestsFromMould这个方法的。。测试套件也是在这一步处理的
elif os.path.isdir(full_path): #dicover里面传脚本目录是走这里。。
if (not namespace and #namespace-默认是false not namespace就是true
not os.path.isfile(os.path.join(full_path, '__init__.py'))): #不是一个包。。-也就是说我们传的目录应该是一个包,下面包含__init__.py
return None, False load_tests = None #这个load_tests是啥意思呢??????????后面继续看----看了一遍--并且用unittest.main()试了一下-模块下面是没有这个属性的。。只是unittest的初始化文件有这个方法--他也是通过discover找的。。
tests = None
name = self._get_name_from_path(full_path) #这里就是走子目录的逻辑了
#get_name_from_path的逻辑在这里就很清晰了
#A.如果通过_find_test 调用self._get_name_from_path 是为了判断两次start_dir是否一致一致返回. 不一致返回从上次的start_dir1找到本次start_dir12的相对路径--
#这里又分两种情况-A1正常情况-start_dir1是start_dir12的上级目录。。。那么返回的那么就是-A.B这样的了。。因为第一次的A是已经os.path.insert到环境变量了..所以A.B是可以直接用
#A2不正常情况 就是之前说的 最少返回一个点的...A这种返回---然后问题来了--他为什么要这么处理呢--原因是?????????
#我猜是与脚本同级存在另一个脚本目录。。。后面验证这一点。-----这里在上面补充了--是因为子目录中还存在脚本所有这么走逻辑-完美的
try:
package = self._get_module_from_name(name) #上面是导入的一个module--这里是导入一个包-- 返回--
except case.SkipTest as e:
return _make_skipped_test(name, e, self.suiteClass), False
except:
error_case, error_message = \
_make_failed_import_test(name, self.suiteClass)
self.errors.append(error_message)
return error_case, False
else:
load_tests = getattr(package, 'load_tests', None) #然后判断这个包里面有没有'load_tests'这个属性---这里我代码一直看下来,我们是不知道这个lood_tests是什么的,字面意思 加载测试集合
# Mark this package as being in load_tests (possibly ;))
self._loading_packages.add(name) #然后把模块名称添加到set集合
try:
tests = self.loadTestsFromModule(package, pattern=pattern)
#这里传了一个package模块对象,和文件匹配规则合作或者文件。。--但是这里导入一个包之后实际上是找不到testCase的-因为包下面的属性肯定不是一个类-不会走loadTestsFromTestCase
#所以这里返回的tests是一个空列表
if load_tests is not None: #貌似这个是弃用的,向后兼容-暂时没看明白这个load_tests代表的意思
# loadTestsFromModule(package) has loaded tests for us.
return tests, False
return tests, True # 如果能走到这里------就返回True_就是给_find_test判断走递归的--_find_tests里面就得到 should_recurse=True
finally:
self._loading_packages.discard(name) #然后这个删掉set集合里面之前导入的那个包
else:
return None, False
def loadTestsFromModule(self, module, *args, pattern=None, **kws):
"""Return a suite of all test cases contained in the given module"""
# This method used to take an undocumented and unofficial
# use_load_tests argument. For backward compatibility, we still
# accept the argument (which can also be the first position) but we
# ignore it and issue a deprecation warning if it's present.
if len(args) > 0 or 'use_load_tests' in kws: #args这个默认是一个空元组 长度默认为0 kws是一个空字典
warnings.warn('use_load_tests is deprecated and ignored',
DeprecationWarning)
kws.pop('use_load_tests', None)
if len(args) > 1:
# Complain about the number of arguments, but don't forget the
# required `module` argument.
complaint = len(args) + 1
raise TypeError('loadTestsFromModule() takes 1 positional argument but {} were given'.format(complaint))
if len(kws) != 0:
# Since the keyword arguments are unsorted (see PEP 468), just
# pick the alphabetically sorted first argument to complain about,
# if multiple were given. At least the error message will be
# predictable.
complaint = sorted(kws)[0] #取出第一个Key-
raise TypeError("loadTestsFromModule() got an unexpected keyword argument '{}'".format(complaint))
tests = []
for name in dir(module): #这里得modul实际上使我们传入的模块对象---dir(object) 返回模块下的所有属性列表-
obj = getattr(module, name) #然后反射返回name对象。。返回的是一个class对象
if isinstance(obj, type) and issubclass(obj, case.TestCase): #这里判断obj是否是一个类--并且这个类是case.TestCase的子类,也就是说 是否写在我们继承unitest.testCase那个类的下面
tests.append(self.loadTestsFromTestCase(obj)) #可以看到最后走loadTestFromTestCase obj这里是传入的一个类名 load_tests = getattr(module, 'load_tests', None)
tests = self.suiteClass(tests)
if load_tests is not None:
try:
return load_tests(self, tests, pattern)
except Exception as e:
error_case, error_message = _make_failed_load_tests(
module.__name__, e, self.suiteClass)
self.errors.append(error_message)
return error_case
return tests #返回集合
def loadTestsFromTestCase(self, testCaseClass): #testCaseClass是我们传的一个用例类
"""Return a suite of all test cases contained in testCaseClass"""
if issubclass(testCaseClass, suite.TestSuite): #这个类是不是suite.TestSuite的子类--如果是的就抛出异常==
raise TypeError("Test cases should not be derived from "
"TestSuite. Maybe you meant to derive from "
"TestCase?")
testCaseNames = self.getTestCaseNames(testCaseClass) #getTestCaseNames 从类下面找到用例名称--找到名称返回的是一个列表
if not testCaseNames and hasattr(testCaseClass, 'runTest'): #这里判断testCaseNames是否为空-并且 是否存在"runTest"这个元素
testCaseNames = ['runTest']
loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) #是我的一个用例类--然后将testCaseNames类下面的测试方法--带进去遍历。。。高级用法---第一次见--这个方法很关键
return loaded_suite
def getTestCaseNames(self, testCaseClass):
"""Return a sorted sequence of method names found within testCaseClass
"""
def isTestMethod(attrname, testCaseClass=testCaseClass, #定义一个内部方法
prefix=self.testMethodPrefix): #self.testMethodPrefix="test" 这个在TestLoader下第一行就已经默认了,他是我们用例开头的固定格式
return attrname.startswith(prefix) and \ #这里判断了是否已test开头以及 方法对象是否可用----getattr返回方法对象--callable()是检测对象是否可用 返回一个布尔值
callable(getattr(testCaseClass, attrname))
testFnNames = list(filter(isTestMethod, dir(testCaseClass)))
#dir(testCaseClass)返回该对象下面所有的属性--包括变量test_1--所以上面需要检测属性时test开头且是一个可以调用的对象--
#filter函数---前面是一个function -后面是一个可迭代对象-会遍历可迭代对象-并传入function-functions返回true则添加到列表--这样就找到了所有的
if self.sortTestMethodsUsing:
testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))
#sortTestMethodsUsing = staticmethod(util.three_way_cmp) 转为为静态方法-内存地址指向self.sortTestMethodsUsing
#然后通过functools这个模块排序==
return testFnNames #然后返回用例方法名称列表
TestLoader源码解析的更多相关文章
- 语义分割丨PSPNet源码解析「测试阶段」
引言 本文接着上一篇语义分割丨PSPNet源码解析「网络训练」,继续介绍语义分割的测试阶段. 模型训练完成后,以什么样的策略来进行测试也非常重要. 一般来说模型测试分为单尺度single scale和 ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
随机推荐
- jpype2
# -*- coding: utf-8 -*-# @Time : 2020/5/21 0:04# 从环境变量获取jvm虚拟机安装路径,若为None则获取默认路径import os import jpy ...
- web自动化之键盘操作
简单介绍下web自动化怎么触发键盘操作 按键操作 需要导入的类from selenium.webdriver.common.keys import Keys 组合键Keys.CONTROL 也就是我们 ...
- leetcode350之实现求解两数组交集(包含重复元素)
给定两个数组,编写一个函数来计算它们的交集. 说明: 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致. 我们可以不考虑输出结果的顺序 def binarySearch(nums, t ...
- Linux实验
实验一 Linux系统安装与简单配置 一.实验目的 1.掌握Linux系统安装的分区准备. 2.掌握Linux系统的安装步骤. 3.掌握Linux系统分区的挂载和卸载. 4.掌握Linux系统的启动和 ...
- burpsuite 2.0beta体验
这里有破解版:http://ximcx.cn/post-110.html 一直再用1.7x版本,2.0的还没怎么用过 移除了 Scanner 和spider 选项卡,全部整理到Dashboard里 代 ...
- 50个SQL语句(MySQL版) 问题三
--------------------------表结构-------------------------- student(StuId,StuName,StuAge,StuSex) 学生表 tea ...
- 网络编程-TCP长连接和短连接
TCP是一个面向连接的协议.无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接.下面会介绍一个TCP连接是如何建立的以及通信结束后是如何终止的. 一.TCP连接的建立与终止 1.1 建立连 ...
- 怎样实现登录?| Cookie or JWT
先问小伙伴们一个问题,登录难吗?"登录有什么难得?输入用户名和密码,后台检索出来,校验一下不就行了."凡是这样回答的小伙伴,你明显就是产品思维,登录看似简单,用户名和密码,后台校验 ...
- Rocket - debug - TLDebugModuleInner - innerCtrl
https://mp.weixin.qq.com/s/7UY99gEJ8QpVBJIohdqKhA 简单介绍TLDebugModuleInner中innerCtrl相关的寄存器. 1. innerCt ...
- Rocket - tilelink - ErrorEvaluator
https://mp.weixin.qq.com/s/NkbW465NAmhDsETksd2M0g 介绍ErrorEvaluator的实现. 1. 基本介绍 ErrorEvalu ...