unittest

作者: 马小跳_ | 来源:发表于2020-07-09 15:43 被阅读0次

    unittest中最核心的四个概念是:test case、test suite、 test runner、 test fixture。

    • 一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。

    • 多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。

    • TestLoader是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例。

    • TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。
      测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。

    • 对一个测试用例环境的搭建和销毁,是一个fixture。

    一个class继承了unittest.TestCase,便是一个测试用例,但如果其中有多个以 test 开头的方法,那么每有一个这样的方法,在load的时候便会生成一个TestCase实例,如:一个class中有四个test_xxx方法,最后在load到suite中时也有四个测试用例。

    到这里整个流程就清楚了:

    写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。在Runner执行时,默认将执行结果输出到控制台,我们可以设置其输出到文件,在文件中查看结果。

    unittest实例

    先来准备一些待测方法:

    mathfunc.py

    def add(a, b):
        return a+b
    
    def minus(a, b):
        return a-b
    
    def multi(a, b):
        return a*b
    
    def divide(a, b):
        return a/b
    

    简单示例:
    test_mathfunc.py

    # -*- coding: utf-8 -*-
    
    import unittest
    from .mathfunc import *
    
    
    class TestMathFunc(unittest.TestCase):
        """Test mathfuc.py"""
    
        def test_1(self):
            """Test method add(a, b)"""
            print(111)
            assert 4 == add(1, 2)
    
        def test_2(self):
            print(222)
    
        def test_3(self):
            print(333)
    
        def test_4(self):
            print(444)
    
    
    if __name__ == '__main__':
        unittest.main(verbosity=2)
    

    再新建一个文件,test_suite.py:

    # -*- coding: utf-8 -*-
    
    import unittest
    
    from unit_test.test_mathfunc import TestMathFunc
    
    if __name__ == '__main__':
        suite = unittest.TestSuite()
    
        tests = [TestMathFunc("test_1"), TestMathFunc("test_2"),]
        suite.addTests(tests)
    
        runner = unittest.TextTestRunner(verbosity=2)
        runner.run(suite)   
    

    执行结果:

    test_2 (unit_test.test_mathfunc.TestMathFunc) ... ok
    test_1 (unit_test.test_mathfunc.TestMathFunc)
    Test method add(a, b) ... ok
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK
    222
    111
    
    Process finished with exit code 0
    

    可以看到,执行情况跟我们预料的一样:执行了2个case,并且顺序是按照我们添加进suite的顺序执行的。

    上面用了TestSuite的 addTests()方法,并直接传入了TestCase列表,我们还可以:

    # 直接用addTest方法添加单个TestCase
    suite.addTest(TestMathFunc("test_multi"))
    
    # 用addTests + TestLoader
    # loadTestsFromName(),传入'模块名.TestCase名'
    suite.addTests(unittest.TestLoader().loadTestsFromName('test_mathfunc.TestMathFunc'))
    suite.addTests(unittest.TestLoader().loadTestsFromNames(['test_mathfunc.TestMathFunc']))  # loadTestsFromNames(),类似,传入列表
    
    # loadTestsFromTestCase(),传入TestCase
    suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
    

    注意,用TestLoader的方法是无法对case进行排序的,同时,suite中也可以套suite。

    将结果输出到文件中

    用例组织好了,但结果只能输出到控制台,这样没有办法查看之前的执行记录,我们想将结果输出到文件。很简单,看示例:

    test_suite.py:

    # -*- coding: utf-8 -*-
    
    import unittest
    from test_mathfunc import TestMathFunc
    
    if __name__ == '__main__':
        suite = unittest.TestSuite()
        suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
    
        with open('UnittestTextReport.txt', 'a') as f:
            runner = unittest.TextTestRunner(stream=f, verbosity=2)
            runner.run(suite)
    

    test fixture之setUp() tearDown()

    上面整个测试基本跑了下来,但可能会遇到点特殊的情况:如果我的测试需要在每次执行之前准备环境,或者在每次执行完之后需要进行一些清理怎么办?比如执行前需要连接数据库,执行完成之后需要还原数据、断开连接。总不能每个测试方法中都添加准备环境、清理环境的代码吧。

    这就要涉及到我们之前说过的test fixture了,修改test_mathfunc.py:

    def setUp(self):
            print "do something before test.Prepare environment."
    
    def tearDown(self):
        print "do something after test.Clean up."
    

    我们添加了 setUp() 和 tearDown() 两个方法(其实是重写了TestCase的这两个方法),这两个方法在每个测试方法执行前以及执行后执行一次,setUp用来为测试准备环境,tearDown用来清理环境.

    如果想要在所有case执行之前准备一次环境,并在所有case执行结束之后再清理环境,我们可以用 setUpClass() 与 tearDownClass():

    class TestMathFunc(unittest.TestCase):
        """Test mathfuc.py"""
    
        @classmethod
        def setUpClass(cls):
            print "This setUpClass() method only called once."
    
        @classmethod
        def tearDownClass(cls):
            print "This tearDownClass() method only called once too."
    

    跳过某个case

    如果我们临时想要跳过某个case不执行怎么办?unittest也提供了几种方法:

    • skip装饰器

      • unittest.skip(reason)、无条件跳过
      • unittest.skipIf(condition, reason)、当condition为True时跳过
      • unittest.skipUnless(condition, reason),当condition为False时跳过
      @unittest.skip("I don't want to run this case.")
      def test_divide(self):
          """Test method divide(a, b)"""
          print "divide"
          self.assertEqual(2, divide(6, 3))
          self.assertEqual(2.5, divide(5, 2))
      
    • TestCase.skipTest()方法

      class TestMathFunc(unittest.TestCase):
      """Test mathfuc.py"""
      
      ...
      
      def test_divide(self):
          """Test method divide(a, b)"""
          self.skipTest('Do not run this.')
          print "divide"
          self.assertEqual(2, divide(6, 3))
          self.assertEqual(2.5, divide(5, 2))
      

    相关文章

      网友评论

          本文标题:unittest

          本文链接:https://www.haomeiwen.com/subject/svlscktx.html