首页 > 代码库 > Python单元测试PyUnit框架轻度整改

Python单元测试PyUnit框架轻度整改

原理

参考:单元测试原理

背景

年后有段时间没写代码了,所以趁着周末找了个python单元测试玩下,测试自己的Android应用。发现PyUnit虽然在单个脚本文件中添加多个测试用例,比如官网提供的方法:官网地址

        import unittest

        class WidgetTestCase(unittest.TestCase):
            def setUp(self):
                self.widget = Widget("The widget")
            def tearDown(self):
                self.widget.dispose()
                self.widget = None
            def testDefaultSize(self):
                assert self.widget.size() == (50,50), incorrect default size
            def testResize(self):
                self.widget.resize(100,150)
                assert self.widget.size() == (100,150),                        wrong size after resize

 

然后结合HTMLTestRunner模块,就可以简单执行一个测试脚本并生成测试报告,如下图(整改之后的):

 

技术分享

 

然而发现有个不好用的地方,每一次执行单元测试脚本后都会生成一个报告,所以有了修改单元测试的想法(增加截图、日志、批量执行所有的脚本并合成一份报告)

思路

查看单元测试报告html源码,发现增加截图、日志比较容易,但是要整合每一个单元测试的报告对我来说就比较难,所以采用下边的方法:

1、直接继承现有的测试报告,在上边增加截图、日志查看功能


2、遍历所有的单元测试脚本名称、类名,动态的加载脚本名称 + 类名 + 方法名到单元测试集合中


3、最后用HTMLTestRunner.HTMLTestRunner模块的run方法执行

搞起

增加截图

根据单元测试流程,最终在HTMLTestRunner._generate_report_test()方法中增加一个截图保存参数

        import CommonLib
        print CommonLib.screenshotPath
        row = tmpl % dict(
            tid = tid,
            Class = (n == 0 and hiddenRow or none),
            style = n == 2 and errorCase or (n == 1 and failCase or none),
            desc = desc,
            ‘‘‘截图参数‘‘‘
            screenshot = str(CommonLib.screenshotPath),
            script = script,
            status = self.STATUS[n],
        )
        rows.append(row)
        if not has_output:
            return

 

在HTMLTestRunner._generate_report_test()方法使用的html模板REPORT_TEST_WITH_OUTPUT_TMPL中增加截图标签。方法:查看单元测试报告源码,里边有个error异常的标签,所以在同级的地方增加截图标签

<a href="%(screenshot)s">screenshot</a>

<a href="%(logcat)s">logdetail</a>

 

效果如上边的单元测试报告截图。增加日志的方法应该差不多

动态加载单元测试依赖的模块

有两种方法:

1、使用importlib动态加载,但是在调试的过程中发现,循环加载模块时会报模块未定义的错,搞了半天没搞定,所以pass掉

2、使用文件遍历的方式,找到所以单元测试脚本名称 + 类名 + 方法名。为了方便,我把每个脚本的名称和类名都定义成一样、所有脚本的测试方法都是teststep()

步骤:

1、遍历单元测试脚本路径下所有脚本并记录脚本名称

def get_file_name(path):
    ‘‘‘
    @see: 遍历单元测试脚本文件下的.py文件并获取.py文件名
    @param path: 单元测试脚本文件路径
    ‘‘‘
    fileNameList = []
    for root, dirs, files in os.walk(path):
        if files:
            for fi in files:
                if init in fi or .pyc in fi:
                    continue
                if .py in fi:
                    fileNameList.append(fi.split(.)[0])
    return fileNameList

 

2、单独写一个单元测试脚本unitTestDemo.py,用来添加所有的testcase到testsuite中

if __name__ == "__main__":
    testsuite = unittest.TestSuite()
    path = CommonLib.unittestcasePath
    ‘‘‘添加单个测试用例到测试集中:‘‘‘
    TestDemo = add_testcase_to_suite(path)
    testsuite.addTests(TestDemo)
def add_testcase_to_suite(path):
    ‘‘‘
    @see: 添加测试用例集合
    ‘‘‘
    fl = Util.get_file_name(path)
    TestDemo = []
    for f in fl:
        ef = f + . + f + ("teststep")
        TestDemo.append(eval(ef))
    return TestDemo

 

3、在脚本unitTestDemo.py中动态添加第一步中获取到的脚本名称 (即依赖的模块名称)

def config_unittest_demo_read(path, moudleName):
    ‘‘‘
    @see: 追加单元测试脚本文件模块到单元测试集合脚本中
    @param path: 单元测试集合脚本文件路径
    @param moudleName: 单元测试脚本依赖的模块
    ‘‘‘
    fl = []
    fo = open(path,r)
    try:
        for f in fo.readlines():
            if from UnitTestDemo import in f:
                p = from.+?\r
                rc = re.compile(p)
                fr = f.replace(rc.findall(f)[0], moudleName + \r)
                fl.append(fr)
                continue
            else:
                fl.append(f)
        return fl
    except IOError,e:
        print e
        return False
    finally:
        fo.close()

 

4、使用HTMLTestRunner.HTMLTestRunner的run方法执行所有的脚本

    ‘‘‘生成测试报告文件‘‘‘
    file_name = CommonLib.unittestresultPath
    fp = file(file_name, wb)
    ‘‘‘执行单元测试‘‘‘
    renner = HTMLTestRunner.HTMLTestRunner(
                                           stream=fp, 
                                           title=测试结果, 
                                           description=测试报告
                                           )
    renner.run(testsuite)

 

命令行方式

为了批量执行方便,还可以使用命令行方式,具体可以查看官方的命令行运行方式。我这里使用bat脚本运行。首先初始化所有要执行的单元测试脚本的依赖,再执行unitTestDemo.py

configUTDemo.bat

cd E:\Python27
python E:\PythonProject\conifgUTDemo.py
ping 127.0.0.1 -n 11>nul

 

startUTTest.bat

cd E:\Python27
python E:\PythonProject\unitTestDemo.py
ping 127.0.0.1 -n 11>nul

 

如果有必要也可以加入到定时任务,定时执行

Python单元测试PyUnit框架轻度整改