unittest原理:https://www.jianshu.com/p/c3fd61ac09e9

因为使用unittest进行管理case的运行。有时case因为偶然因素,会随机的失败。通过重试机制能够补充保持case的稳定性。查阅资料后发现,python的unittest自身无失败重试机制,可以通过以下手段达到目的:1.修改unittest源码,使test case重新运行若干次 2. 对case结果进行处理,单独调度运行失败的case。此篇我们来了解下如何通过修改源码进行失败重试。使用的python版本为2.7。

* 百度上搜索失败重试,找到这个文章。http://blog.csdn.net/hqzxsc2006/article/details/50349664。我尝试了此方法,并对其中部分逻辑进行了修改。后来发现我需要的运行场景中此方法无法满足要求,随放弃了该方法。

1. 调用unittest运行case

def TestSuite(filedirname):
loader = unittest.TestLoader()
dir_case = loader.discover(start_dir=config.case_dir, pattern=filedirname, top_level_dir=None)
return dir_case if __name__ == '__main__':
filedirname = 'Test*' # 需要执行的case
unittest.TextTestRunner(verbosity=2).run(TestSuite(filedirname))

2. 定位运行case的源码1

  以上为调用unittest的方法。通过loader.discover扫描指定dir中所有以Test打头的文件,加载所有的case。然后通过unittest.TextTestRunner(verbosity=2).run()方法来运行所有的case。进入 TextTestResult类查看run方法,理解代码后发现:先初始化运行结果result,然后初始化startTestRun,运行test(result)后,处理result里记录的结果。通过加入打印等方式发现,此处的test(result),将加载的所有case都运行了。所以无法在此处做失败重试。

3. 定位运行case的源码2

  通过函数定义可看出:TextTestResult.run(self, test)中的test即为:TestSuite(filedirname)函数的返回值。查看TestLoader.discover()函数,发现返回类型为:TestSuite。此类的run方法,即为搜索到的文章所修改的代码处。源码不支持中文,注释中有中文也不行,否则运行case将报错。以下为了写文章方便,使用中文注释。

class TestSuite(BaseTestSuite):
def run(self, result, debug=False):
failcount = 1
class_num = 1
topLevel = False
if getattr(result, '_testRunEntered', False) is False:
result._testRunEntered = topLevel = True for test in self:
case_num = 1
if result.shouldStop:
break success_flag = True
while success_flag:
if _isnotsuite(test):
self._tearDownPreviousClass(test, result)
self._handleModuleFixture(test, result)
self._handleClassSetUp(test, result)
result._previousTestClass = test.__class__
if (getattr(test.__class__, '_classSetupFailed', False) or
getattr(result, '_moduleSetUpFailed', False)):
if class_num > failcount:
success_flag = False
else:
time.sleep(5)
result._previousTestClass = None
print 'Class %s retry time(s) %s'%(test.__class__,class_num)
class_num += 1
continue
          f,e = map(len, (result.failures, result.errors)) #查看result类得知失败和错误的保存在此
          cntBefore = f + e
if not debug:
test(result)
else:
test.debug()          f,e = map(len, (result.failures, result.errors))
         cntAfter = f + e 
if cntAfter > cntBefore:
if case_num > failcount:
success_flag = False
else:
print 'Test % retry time(s): %s'%(test,case_num)
case_num += 1
else:
success_flag = False if topLevel:
self._tearDownPreviousClass(None, result)
self._handleModuleTearDown(result)
result._testRunEntered = False
return result

  实验中,通过以下方法调用,使用ok:

def suit():
s = unittest.TestSuite()
s.addTest(Test_map("test_xxx1"))
s.addTest(Test_map("test_xxx2"))
unittest.TextTestRunner().run(s) if __name__ == '__main__':
suit()

  当我以为问题解决时,发现通过1中的unittest.TextTestRunner(verbosity=2).run(TestSuite(filedirname))执行时,即行不通。当case失败时,就一直重试,进入死循环。debug时,发现当return result后,代码再一次进入了test(result),并且case_num=1,success_flag=True。所以进入循环,无法出来了。为何return后又进入test(result)了呢,我没理解出来....

3. 定位运行case源代码3

  继续寻找合适的位置修改代码。debug时发现,TestSuite.run中的test(result)方法,实际上运行的是case.py中的TestCase.run方法。在此处修改也应可以实现重试。run中的其他代码保持不变。

            retryCnt = 0
retryMax = 3 success = False
while (not success) and (retryCnt < retryMax): #此处加入一个循环
print "RetryCnt:", retryCnt
try:
self.setUp()
except SkipTest as e:
self._addSkip(result, str(e))
except KeyboardInterrupt:
raise
except:
result.addError(self, sys.exc_info())
else:
try:
testMethod()
except KeyboardInterrupt:
raise
except self.failureException:
result.addFailure(self, sys.exc_info())
except _ExpectedFailure as e:
addExpectedFailure = getattr(result, 'addExpectedFailure', None)
if addExpectedFailure is not None:
addExpectedFailure(self, e.exc_info)
else:
warnings.warn("TestResult has no addExpectedFailure method, reporting as passes",
RuntimeWarning)
result.addSuccess(self)
except _UnexpectedSuccess:
addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
if addUnexpectedSuccess is not None:
addUnexpectedSuccess(self)
else:
warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures",
RuntimeWarning)
result.addFailure(self, sys.exc_info())
except SkipTest as e:
self._addSkip(result, str(e))
except:
result.addError(self, sys.exc_info())
else:
success = True try:
self.tearDown()
except KeyboardInterrupt:
raise
except:
result.addError(self, sys.exc_info())
success = False cleanUpSuccess = self.doCleanups()
success = success and cleanUpSuccess
if success:
result.addSuccess(self)
else: #此次为新加入的代码
retryCnt += 1
print "----run case failed---"

  可以看到,基本上只是加入了一个循环和else分支。运行后,得到了与预期基本一致的结果。case A若失败,会重新运行,直到成功。每个case最多运行retryMax次。结果中记录每次运行的结果,如case A运行失败1次,成功1次。

python unittest case运行失败重试的更多相关文章

  1. python修改python unittest的运行顺序

    正常是一个测试类中按函数名字运行, 下面修改成直接按每个测试方法的代码顺序执行 文件 unittest_util.py import time import unittest from app.uti ...

  2. 第二种方式,修改python unittest的执行顺序,使用猴子补丁

    1.按照测试用例的上下顺序,而不是按方法的名称的字母顺序来执行测试用例. 之前的文章链接 python修改python unittest的运行顺序 之前写的,不是猴子补丁,而是要把Test用例的类名传 ...

  3. Selenium2+python自动化66-装饰器之运行失败截图【转载】

    前言 对于用例失败截图,很多小伙伴都希望在用例执行失败的时候能自动截图,想法是很好的,实现起来并不是那么容易. 这里分享下我的一些思路,当然目前还没找到完美的解决方案,我的思路是用装饰器去解决,希望有 ...

  4. 白话陈述之——从python脚本变化解析由路径引起的GP服务运行失败问题

    补充一下未完待续的利用Python分析GP服务运行结果的输出路径 & 实现服务输出路径的本地化,这篇博客中主要介绍了如何实现将GP服务生成的结果输出至本地及输入输出路径导致GP服务运行失败的问 ...

  5. 稳定UI运行结果-自动化测试失败重试和截图

    运行自动化测试的时候,有时会因为网络不稳定,测试环境或者第三方环境正在重启而造成用例运行结果不稳定,时而能跑过时而跑不过.这些难以重现的环境因素造成的用例失败会让测试人员很困扰,排查即耗费时间也没有太 ...

  6. 解决python在cmd运行时导入包失败,出现错误信息 "ModuleNotFoundError: No module named ***"

    1.下图为我的自动化测试工程结构图 我通过运行run.bat批处理文件,调用cmd控制台运行start_run.py来开始我的自动化测试,但是出现如下错误: 大家可能知道我们的工程在IDE(Pycha ...

  7. python unittest addCleanup中也加失败截图功能

    在python web自动化测试中失败截图方法汇总一文中提到了失败截图的方法 但在实际测试中,如果我们的测试用例中加了addCleanups动作,如果addCleanups中动作失败了,就不会截图.那 ...

  8. 使用Python请求http/https时设置失败重试次数

    设置请求时的重试规则 import requests from requests.adapters import HTTPAdapter s = requests.Session() a = HTTP ...

  9. Python Unittest简明教程

    1 概述 单元测试框架是一种软件测试方法,通过来测试源代码中的各个单元,例如类,方法等,以确定它们是否符合要求.直观上来说,可以将单元视为最小的可测试部分.单元测试是程序员在开发过程中创建的短代码片段 ...

随机推荐

  1. 【pycharm】pycharm上安装tensorflow,报错:AttributeError: module 'pip' has no attribute 'main' 解决方法

    pycharm上安装tensorflow,报错:AttributeError: module 'pip' has no attribute 'main' 解决方法 解决方法: 在pycharm的安装目 ...

  2. asp.net mvc5 安装

    原文地址 http://docs.nuget.org/docs/start-here/using-the-package-manager-console 工具-->NuGet程序包管理器--&g ...

  3. LaTeX技巧24:LaTeX常用命令集锦

    \hyphenation{word list} %断字命令:\showthe\topmargin %显示某个参数的数值或者内容: 在tex编译过程中出现行溢出(overflow hbox)是由于断字程 ...

  4. idea创建springcloud项目图文教程(EurekaServer注册中心)

    http://blog.csdn.net/hcmony/article/details/77854999 idea创建springcloud项目图文教程(EurekaServer注册中心)(六) 1, ...

  5. Netty Associated -- Channel

    A nexus to a network socket or a component which is capable of I/O operations such as read, write, c ...

  6. 整理:FPGA选型

    针对性整理下FPGA选型问题 一.获取芯片资料: 要做芯片的选型,首先就是要对有可能要面对的芯片有整体的了解,也就是说要尽可能多的先获取芯片的资料.现在FPGA主要有4个生产厂家,ALTERA,XIL ...

  7. 迭代器模式(Iterator)

    @@@模式定义: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示. @@@练习示例:  工资表数据的整合 @@@示例代码: \pattern\PayModel.java ~ ...

  8. 在Win7 Hyper-v虚拟机中挂接真实机的声卡

    最近在测试Lync的语音功能,环境已在虚拟机中搭建好,但Hyper-V中不支持声卡,一直未测试成功,经过一番尝试后终于成功,细节如下: 1.真机为Win7 企业版,虚拟机也是Win7的企业版.(虚拟机 ...

  9. 阅读日志: 微信小程序的风口只有一天?

    原文地址: 小程序的风口只有一天?难道我们用的是假的小程序 http://xudanei.baijia.baidu.com/article/755506 原文摘要: #,小程序的趋势,从1月10日至1 ...

  10. CSS-页面滑屏滚动原理

    现在的网站有的时候为了简洁就是很多的单页滑屏滚动介绍,主要呈现方式有两种,一种是整体的元素一直排列下去,假设有五个需要展示的全屏页面,那么高度是500%,只是展示100%,剩下的可以通过transfo ...