unittest笔记

背景

1. 学习资料: 

官网: https://docs.python.org/2.7/library/unittest.html

https://docs.python.org/3/library/unittest.html

IBM Python自动单元测试框架: http://www.ibm.com/developerworks/cn/linux/l-pyunit/

2. python其他类似的工具

doctest: https://docs.python.org/2.7/library/doctest.html#module-doctest

unittest2: https://pypi.python.org/pypi/unittest2

nose: https://nose.readthedocs.org/en/latest/

3. 定义

unittest,有时也叫"PyUnit",是Python语言版本的单元测试框架。和JAVA语言的JUnit类似。

 

unittest 主要分为以下几个模块:

test fixture:

指的是测试之前需要做的准备工作以及相关的清理工作。比如,新建暂时数据库或代理数据库,目录,或者启动服务器进程。这里主要就是指 setup 以及teardown 函数。

test case(TestCase):
test case是测试的最小单元。它检查一组特定输入的结果。unittest提供一个基类,TestCase,我们可以使用TestCase来新建新的测试用例。

test suite(TestSuite):
test suite是test case的集合,也可以是test suite的集合。它用来将所有一起执行的测试集成在一起。

test runner(TestRunner):
test runner负责执行测试并且提供测试结果。runner可能使用图形接口、文字接口、或者返回一个特殊值来指定测试结果。

示例

1. 测试用例组成(TestCase)

所有的测试用例必须继承自类 unittest.TestCase ,一般测试用例方法名以 test开头。一个测试用例类内部可以定义一个或多个测试用例方法,比如下面就定义了3个方法。

import unittest

class TestStringMethods(unittest.TestCase):
    
    def setUp(self):
        print("This is setup")

    def test_upper(self):
        print("This is test_upper")
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        print("This is test_isupper")
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        print("This is test_split")
        s = 'hello world'
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            s.split(2)
            
    def tearDown(self):
        print("This is tearDown")

if __name__ == '__main__':
    unittest.main()

脚本上右键,选择 Run -> Run as Python unit-test ,就可以看到控制台输出如下:

Finding files... done.
Importing test modules ... done.

This is setup
This is test_isupper
This is tearDown
This is setup
This is test_split
This is tearDown
This is setup
This is test_upper
This is tearDown
----------------------------------------------------------------------
Ran 3 tests in 0.004s

OK

可以看到,每个测试用例方法执行之前都会调用 setUp方法,每个测试用例方法执行结束都会调用 tearDown方法。

一般会把测试用例执行前的准备工作放到setUp方法里面,比如WEB测试的登录操作。setUp执行时如果抛出异常,后边的test都不会被执行,该用例会被标记为error。

一般会把测试用例执行后的清理工作放到tearDown方法里面,比如WEB测试的关闭浏览器操作。只要setUp执行成功,tearDown就会被执行。tearDown抛出异常的话,测试结果会被标记为error。

2. 测试结果判定

 unittest.TestCase 类包含各种判定测试结果的方法。

比如上面我们使用过的“self.assertEqual('foo'.upper(), 'FOO')”。

MethodChecks thatNew in
assertEqual(a, b) == b  
assertNotEqual(a, b) != b  
assertTrue(x) bool(x) is True  
assertFalse(x) bool(x) is False  
assertIs(a, b) is b 3.1
assertIsNot(a, b) is not b 3.1
assertIsNone(x) is None 3.1
assertIsNotNone(x) is not None 3.1
assertIn(a, b) in b 3.1
assertNotIn(a, b) not in b 3.1
assertIsInstance(a, b) isinstance(a, b) 3.2
assertNotIsInstance(a, b) not isinstance(a, b) 3.2

 

所有的assert方法都含有一个默认参数“msg”,该参数默认值为“None”。如果给msg传值,在assert判定fail的时候,系统就会打印出msg作为fail message。

“self.assertEqual('foo'.upper(), 'FOO1',"foo.upper is not FOO1!")”

如果assert方法判定特定条件不满足,就会抛出一个异常 AssertionError,unittest执行结果就会被判定为 fail。

3. 测试用例集(TestSuite)

多个TestCase可以组成一个TestSuite 。一般实际测试都有很多个TestCase,我们需要对其进行组合,这样方便执行以及收集结果。

跑一个TestSuite ,和单独跑TestSuite里面的所有的TestCase ,结果是一样的。

多个TestSuite 也可以组成一个更大的TestSuite。

import unittest

class TestStringMethods(unittest.TestCase):
    # 类内容参见上面的代码
        
def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestStringMethods('test_upper'))
    suite.addTest(TestStringMethods('test_split'))
    return suite

if __name__ == '__main__':
    print(suite())

执行结果如下:

<unittest.suite.TestSuite tests=[<__main__.TestStringMethods testMethod=test_upper>, 
<__main__.TestStringMethods testMethod=test_split>]>

可以看到suite()返回的是一个 TestSuite实例,里面只含有我们添加进去的测试用例方法。

TestSuite 主要包含4个方法:

addTest(test):添加test到TestSuite。这里的test可以是一个TestCase,也可以是一个TestSuite。

addTests(tests):添加多个test到TestSuite。这里的tests 可以是一个 TestCase列表,也可以是一个TestSuite列表。

run(result):跑TestSuite,并且收集测试结果到result。

countTestCases():返回该TestSuite说包含的所有的TestCase数目。

4. TestLoader

该类的目的就是为了更方便的整合所有testCase 为 TestSuite。

一般都不需要实例化一个TestLoader对象,可以直接通过 unittest.defaultTestLoader 来获得一个 TestLoader 实例。

 

该类主要包含的都是loadTest方法,就是通过各种方式来收集TestCase 。 方法返回的是一个TestSuite 实例 。

loadTestsFromTestCase(testCaseClass):通过TestCase的类名加载用例。比如 TestStringMethods 。

loadTestsFromModule(module, pattern=None):通过模块名加载用例。

loadTestsFromName(name, module=None):通过名称加载用例。该名称一般是“xxx.xxxx.xxx.xxx”的形式,可以具体到TestCase类名(比如 TestStringMethods),也可以具体到类中的方法名(比如 test_split )。

loadTestsFromNames(names, module=None):通过多个名称加载用例。类似 loadTestsFromName ,只不过这里的参数是一个集合,比如一个列表。

discover(start_dir, pattern='test*.py', top_level_dir=None):通过路径加载用例。 start_dir 一般是一个大的路径。pattern一般是具体脚本的匹配模式,默认是所有以test开头的脚本。

使用示例:

import unittest

class TestStringMethods(unittest.TestCase):
    #类内容参见上面的代码
        
def suite():
    suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestStringMethods)
    return suite

if __name__ == '__main__':
    print(suite())

输出结果如下:

<unittest.suite.TestSuite tests=[<__main__.TestStringMethods testMethod=test_isupper>, 
<__main__.TestStringMethods testMethod=test_split>,
<__main__.TestStringMethods testMethod=test_upper>]>

可以看到返回的是一个TestSuite实例。

 5. TestResult

 该类用来收集测试结果。所有的测试结果都可以通过TestResult获取。

import unittest

class TestStringMethods(unittest.TestCase):
    #类内容参见上面的代码

def suite():
    suite =unittest.defaultTestLoader.loadTestsFromTestCase(TestStringMethods)
    return suite

if __name__ == '__main__':
    my_result=unittest.TestResult()
    my_suite=suite()
    my_suite.run(my_result)
    print("My Result:",my_result)
    print("Errors:",my_result.errors)
    print("Failures:",my_result.failures)
    print("Total Run case:",my_result.testsRun)

输出结果:

My Result: <unittest.result.TestResult run=3 errors=0 failures=1>
Errors: []
Failures: [(<__main__.TestStringMethods testMethod=test_upper>, 'Traceback (most recent call last):\n  File "E:\\workspace\\Test\\src\\test\\test_unittest.py", line 10, in test_upper\n    self.assertEqual(\'foo\'.upper(), \'FOO1\',"oooFail")\nAssertionError: \'FOO\' != \'FOO1\'\n- FOO\n+ FOO1\n?    +\n : oooFail\n')]
Total Run case: 3

errors : 返回一个列表。如果测试Pass,则列表为空。不然就是所有的error集合。

failures  : 返回一个列表。如果测试Pass,则列表为空。不然就是所有的failure集合。比如上面我故意更改 test_upper的assert判定条件,导致该用例fail。这里就记录了failure的情况。

testsRun:返回一个整数。值为所有的run的测试用例数目。比如这里就是3。

TestResult 还含有一些方法,感觉主要作用就是定义自己的TestResult时候可以重写这些方法。

startTest(test) :将要执行测试用例方法的时候调用。

stopTest(test) :测试用例方法执行完毕后调用。

startTestRun() :执行第一个用例前调用。

stopTestRun() :执行最后一个用例后调用。

addError(test, err) :测试用例返回error时调用。

addFailure(test, err) :测试用例返回fail时调用。

addSuccess(test) :测试用例返回pass时调用。

6. 命令行执行

示例:

python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
python -m unittest tests/test_something.py

可以采用 “python -m unittest xxxx” 的方式来执行测试用例。 xxxx可以是模块、用例类、用例类中的方法、或者文件路径。

>python -m unittest test_unittest.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

如果想获取更详细的内容,可以加 -v 。

>python -m unittest -v test_unittest.py
test_isupper (tet_excel.TestStringMethods) ... ok
test_split (tet_excel.TestStringMethods) ... ok
test_upper (tet_excel.TestStringMethods) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.002s

OK

也可以使用discover来自动加载测试用例。但是前提是脚本名称必须以 test开头。

discover 命令还有其他的一些使用方法,比如自定义匹配模式等等。这里不细说。

>cd project_directory

>python -m unittest discover
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK

7. 跳过执行

如果某个测试方法不想执行,给它加上 skip()装饰器即可。

示例:

import unittest
import sys
from common import mylib

class MyTestCase(unittest.TestCase):

    @unittest.skip("demonstrating skipping")
    def test_nothing(self):
        self.fail("shouldn't happen")

    @unittest.skipIf(mylib.__version__< 5,
                     "not supported in this library version")
    def test_format(self):
        # Tests that work for only a certain version of the library.
        pass

    @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
    def test_windows_support(self):
        # windows specific testing code
        pass

if __name__ == '__main__':
    unittest.main(verbosity=2)

执行结果如下:

test_format (__main__.MyTestCase) ... skipped 'not supported in this library version'
test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping'
test_windows_support (__main__.MyTestCase) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK (skipped=2)

可以看到前面两个用例都被跳过了。但是因为我的环境时window的,所以第3个用例没有被跳过。

如果某个测试用例类不想执行,也可以跳过。

import unittest

@unittest.skip("showing class skipping")
class MySkippedTestCase(unittest.TestCase):
    def test_not_run(self):
        pass
    
    def test_not_run2(self):
        pass

if __name__ == '__main__':
    unittest.main(verbosity=2)

执行结果如下:

test_not_run (__main__.MySkippedTestCase) ... skipped 'showing class skipping'
test_not_run2 (__main__.MySkippedTestCase) ... skipped 'showing class skipping'

----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK (skipped=2)

可以看到两个测试用例方法都被跳过了。

8. TestProgram

关于命令行执行的一个测试类。没看到官方文档有详细的资料,暂时不研究。

posted @ 2015-11-25 17:03  微微微笑  阅读(729)  评论(0编辑  收藏  举报