[Python unittest] 3-Organizing test code

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Python unittest] 3-Organizing test code相关的知识,希望对你有一定的参考价值。

 

  • 组织测试代码

    前面已经了解到测试的原理和步骤,但只是默认类string的测试,如果是我们自己写的类改怎么测试呢?

    如下

    class Widget(object):
        def __init__(self,name,width=50,height=50):
            self.name = name
            self.width = width
            self.height = height
    
        def __repr__(self):
            return "Widget({0})".format(self.name)
        
        # 返回大小
        def size(self):
            return (self.width, self.height)
        
        #重设大小
        def resize(self, *args):
            try:
                self.width = args[0]
                self.height = args[1]
            except:
                pass
            return self.size()
        
        #其他方法
        def dispose(self):
            print ‘这里是tearDown‘
            pass
    

     测试用例可以这样写

    import unittest
    
    class DefaultWidgetSizeTestCase(unittest.TestCase):
        def runTest(self):
            widget = Widget(‘The widget‘)
            self.assertEqual(widget.size(), (50, 50), ‘incorrect default size‘)
    

     结果

    python -m unittest users.tests.DefaultWidgetSizeTestCase
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.000s
    
    OK
    

     实际情况中,如果对该类我们有一百个测试用例需要写,难道要写100次widget = Widget(‘The widget‘)吗?我们猿类的服务宗旨是什么?从不写重复的代码
    所以unittest给我们提供了setup,setup是每一个测试用例runTest之前都会执行的函数,相当于给我们提供了一个准备环境的方法

    import unittest
    
    class SimpleWidgetTestCase(unittest.TestCase):
        def setUp(self):
            self.widget = Widget(‘The widget‘)

    class DefaultWidgetSizeTestCase(SimpleWidgetTestCase): def runTest(self): self.assertEqual(self.widget.size(), (50,50), ‘incorrect default size‘) class WidgetResizeTestCase(SimpleWidgetTestCase): def runTest(self): self.widget.resize(100,150) self.assertEqual(self.widget.size(), (100,150), ‘wrong size after resize‘)

     

    python -m unittest users.tests
    ..
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK
    

     两个有 runTest 的测试用例都自动执行了父类的setup函数,当setup出错后,测试用例就不会执行了

    相似的,unittest提供了在测试用例执行之后可以自动执行的方法tearDown,可以让我们做一些自己需要的事

    class SimpleWidgetTestCase(unittest.TestCase):
        def setUp(self):
            self.widget = Widget(‘The widget‘)
    
        def tearDown(self):
            self.widget.dispose()
            self.widget = None
    

     

    python -m unittest users.tests
    这里是tearDown
    .这里是tearDown
    .
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK
    

     只要setup成功,不论测试用例是否正确,tearDown都会执行
    现在每一个测试用例都要继承相同的测试夹具,这让人感觉很不舒服,unittest提供了如下办法,很像java的JUnit

    class WidgetTestCase(unittest.TestCase):
        def setUp(self):
            self.widget = Widget(‘The widget‘)
    
        def tearDown(self):
            self.widget.dispose()
            self.widget = None
    
        def test_default_size(self):
            self.assertEqual(self.widget.size(), (50,50),
                             ‘incorrect default size‘)
    
        def test_resize(self):
            self.widget.resize(100,150)
            self.assertEqual(self.widget.size(), (100,150),
                             ‘wrong size after resize‘)
    

     

    python -m unittest users.tests.WidgetTestCase
    这里是tearDown
    .这里是tearDown
    .
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK
    

     这里没有runTest函数,而是用test_开头的方法代替,每一个test_开头的方法都会被当作一个测试独立运行,包括setup和tearDown也是独立的
    如果只想运行部分测试用例改怎么办呢?

    if __name__ == ‘__main__‘:
        widgetTestSuite = unittest.TestSuite()
        widgetTestSuite.addTest(WidgetTestCase(‘test_default_size‘))
        widgetTestSuite.addTest(WidgetTestCase(‘test_resize‘))
    
        #suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
    
        unittest.TextTestRunner(verbosity=2).run(widgetTestSuite)
    

     将你想运行的测试用例加入TestSuite

    python */users/tests.py
    test_default_size (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    test_resize (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.001s
    
    OK
    

     或者将你想测试的类加载为suite,可以测试整个类的所有用例

    if __name__ == ‘__main__‘:
        #widgetTestSuite = unittest.TestSuite()
        #widgetTestSuite.addTest(WidgetTestCase(‘test_default_size‘))
        #widgetTestSuite.addTest(WidgetTestCase(‘test_resize‘))
    
        suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
    
        unittest.TextTestRunner(verbosity=2).run(suite)
    

     

    python */users/tests.py
    test_default_size (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    test_resize (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.001s
    
    OK
    

     更美观的做法是

    if __name__ == ‘__main__‘:
        def suite():
            suite = unittest.TestSuite()
            suite.addTest(WidgetTestCase(‘test_default_size‘))
            suite.addTest(WidgetTestCase(‘test_resize‘))
            return suite
    
    
        def suite_map():
            tests = [‘test_default_size‘, ‘test_resize‘]
            return unittest.TestSuite(map(WidgetTestCase, tests))
    
    
        unittest.TextTestRunner(verbosity=2).run(suite())
    

     

    python */users/tests.py
    test_default_size (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    test_resize (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s
    
    OK
    

     有时候需要阻止各个测试用例,很简单测试套件TestSuite像TestCase一样被加入TestSuite

    if __name__ == ‘__main__‘:
        def suite():
            suite = unittest.TestSuite()
            suite.addTest(WidgetTestCase(‘test_default_size‘))
            suite.addTest(WidgetTestCase(‘test_resize‘))
            return suite
    
    
        def suite_map():
            tests = [‘test_default_size‘, ‘test_resize‘]
            return unittest.TestSuite(map(WidgetTestCase, tests))
    
        alltest = unittest.TestSuite([suite(), suite_map()])
    
        unittest.TextTestRunner(verbosity=2).run(alltest)
    

     

    python */users/tests.py
    test_default_size (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    test_resize (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    test_default_size (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    test_resize (__main__.WidgetTestCase) ... 这里是tearDown
    ok
    
    ----------------------------------------------------------------------
    Ran 4 tests in 0.001s
    
    OK
    

     

  测试代码可以放在任何地方,不过有几个原则需要注意:

  •   测试模块可以独立在命令行执行
  •   测试代码可以很容易的从项目中分离出来 
  •        没有合理的理由不要改变测试代码来适应它所测试的代码
  •        代码的修改应该比测试代码的修改频繁的多
  •        被测试代码重构更容易
  •        用C编写的模块的测试无论如何必须是独立的模块,那么为什么不一致呢?
  •        测试策略发生变化,不需要更改源代码









以上是关于[Python unittest] 3-Organizing test code的主要内容,如果未能解决你的问题,请参考以下文章

python+selenium+unittest测试框架1-unittest单元测试框架和断言

python unittest学习笔记

python unittest用例执行方式

Python unittest模块心得

python unittest基本思路

Python中的unittest和logging