Python3 unittest,入门

1 介绍

unittest借鉴了JUnit的思想,支持测试代码的初始化(setup)和回收(teardown),测试集,以及测试报告框架与测试用例的独立性

The unittest unit testing framework was originally inspired by JUnit and has a similar flavor as major unit testing frameworks in other languages. It supports test automation, sharing of setup and shutdown code for tests, aggregation of tests into collections, and independence of the tests from the reporting framework.

几个重要的概念

  • test fixture 测试固件,用作测试用例的前期准备和后期清理;有的翻译成测试夹具,前后的fixture将用例夹在中间,比较形象
  • test case 测试用例,可以是一个小的测试点,也可以是包含多个测试点的对象;unittest中提供TestCase基类为衍生新的test cases
  • test suite 测试套件,即test cases的集合
  • test runner 执行器,启动测试执行,并输出测试结果,类似于流水线

2 TestCase

定义class,继承unittest.TestCase

测试方法必须以test开头,如test_case1, test_case2…

__init__, setUp, tearDown每个方法均会调用一次

另外支持class级别的setUpClass, tearDownClass

实现参数化,可以借助subTest()

class MyTestCase(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print('init ')

    @classmethod
    def setUpClass(cls):
        print('class setup')

    @classmethod
    def tearDownClass(cls):
        print('class teardown')

    def setUp(self):
        print('setup')

    def tearDown(self):
        print('teardown')

    def test_case1(self):
        print('test case1')
        self.assertTrue(True)

    def test_case2(self):
        print('test case2')
        self.assertFalse(False)

    @unittest.expectedFailure
    def test_fail(self):
        self.assertEqual(1, 0, 'broken')

    def test_case4(self):
        for i in range(6):
            with self.subTest(v = i):
                self.assertTrue(i % 2 == 0)

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

3 TestSuit

TestSuit可以嵌套,因此可以实现分组功能

if __name__ == "__main__":
    suite = unittest.TestSuite();
    suite.addTest(MyTestCase('test_case1'))
    suite.addTest(MyTestCase('test_case2'))

    runner = unittest.TextTestRunner()
    runner.run(suite)

4 程序入口

执行下面的语句将测试当前的TestCase,跟踪代码发现就是实例化TestProgram

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

重点看TestProgram.__init__方法,

self.parseArgs(argv)   //初始化参数,加载用例下的测试方法
self.runTests()    //执行测试用例

4-1 加载测试方法

具体如何加载测试方法的,请继续往下看

执行createTests方法,可见方法由加载器从module内读取

self.test = self.testLoader.loadTestsFromModule(self.module)

先看loadTestsFromModule

//使用dir方法查看module内的所有属性、方法、类等
//如果是TestCase的子类,则从中提取测试方法
for name in dir(module):
	obj = getattr(module, name)
	if isinstance(obj, type) and issubclass(obj, case.TestCase):
		tests.append(self.loadTestsFromTestCase(obj))

再来看loadTestsFromTestCase是如何从TestCase中读取method的

  1. 同样使用dir查看TestCase与的属性和方法
  2. 通过isTestMethod方法过滤,是否可调用(callable,即方法)并且是否以test开头(testMethodPrefix默认=test,所以要求测试方法必须以test开头,否则无法识别)
  3. 按字典顺序排序
def getTestCaseNames(self, testCaseClass):
	def isTestMethod(attrname, testCaseClass=testCaseClass,
					 prefix=self.testMethodPrefix):
		return attrname.startswith(prefix) and \
			callable(getattr(testCaseClass, attrname))
	testFnNames = list(filter(isTestMethod, dir(testCaseClass)))
	if self.sortTestMethodsUsing:
		testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))
	return testFnNames

最后调用TestCase(methodName)实例化,所以有多少方法就会实例化多个个case对象,即执行__init__

4-2 执行流程

  • 初始化runner.TextTestRunner,直接将测试结果以文本形式输出在控制台

  • 执行TestSuite的run方法

    • 清理上一个Case
    • 执行setModule固件
    • 执行setUpClass固件
    • 执行TestCase的run方法
      • 检查skip判断
      • 执行setUp固件
      • 如果setUp成功,则执行method方法
      • 执行tearDown固件
      • 执行最终的doCleanups()
    • 执行tearDownClass固件
    • 执行tearDownModulel固件
    for index, test in enumerate(self):
    	if result.shouldStop:
    		break
    
    	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)):
    			continue
    
    	if not debug:
    		test(result)
    	else:
    		test.debug()
    
    	if self._cleanup:
    		self._removeTestAtIndex(index)
    
    if topLevel:
    	self._tearDownPreviousClass(None, result)
    	self._handleModuleTearDown(result)
    	result._testRunEntered = False
    

5 命令行执行

取自官网,可以传入module,TestCase类,甚至TestCase下的具体方法

python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method

如果未传入测试对象参数,将启动自动加载TestLoader.discover()

  • -s 指定开始检索的目录,默认为’.’
  • -p 指定module的匹配模式,默认为‘test*.py’
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值