unittest使用總結


 

unittest簡介

Unittest是python內置的一個單元測試框架,主要用於自動化測試用例的開發與執行

簡單的使用如下

import unittest

class TestStringMethods(unittest.TestCase):

    def setUp(self):
        print("test start")

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def tearDown(self):
        print("test end") 

if __name__ == '__main__':
    unittest.main()
  • 1.導入unittest庫
  • 2.創建類繼承TestCase類
  • 3.以test開頭的方法,就是實際執行的獨立用例,必須要以test開頭,因為是unittest中約定的
  • 4.setUp()方法用於測試用例執行前的初始化工作,tearDown()方法用於用例執行完后的清理操作,這里用例指以test開頭的方法,也就是每個test開頭的方法執行前后都會調用這兩個方法
  • 5.assertEqual等是TestCase類斷言的方法,實際就是簡單的比較並拋出異常
  • 6.main()方法提供了一個測試腳本的命令行接口,可以在腳本內直接運行

運行測試

1.使用命令行python -m unittest xxx腳本名

2.有unittest.main()就直接執行腳本

結果

----------------------------------------------------------------------
test start
test end
.test start
test end
.
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

 

主要結構

整體結構:unittest庫提供了Test Case, Test Suite, Test Runner, Test Fixture

  • Test Case:通過繼承TestCase類,創建一個測試用例集,但這個測試用例集里面可能包含多個測試用例(或者測試步驟)即test開頭的方法
  • Test Suite:把多個測試用例集合在一起來執行。可以通過addTest加載TestCase到Test Suite中,從而返回一個TestSuite實例。
  • Test Runner:Test Runner是一個用於執行和輸出測試結果的組件,可以使用圖形界面,文本界面,或者返回一個特殊的值的方式來表示測試執行的結果。
  • Test Fixture:提供一些腳手架類的方法,常用於測試環境的設置與清理。

 

構建用例

構建用例的方法主要就是繼承TestCase類,創建自己的測試類,然后用約定的test開頭命名方法,這些方法就是測試用例

class TestStringMethods(unittest.TestCase):

    #用例以test開頭
    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

寫用例的時候常使用斷言,主要斷言有以下:

斷言方法 檢查條件
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertlsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)

還有判斷數據類型的斷言:

斷言方法 用於比較的類型
assertMultiLineEqual(a, b) 字符串(string)
assertSequenceEqual(a, b) 序列(sequence)
assertListEqual(a, b) 列表(list)
assertTupleEqual(a, b) 元組(tuple)
assertSetEqual(a, b) 集合(set 或 frozenset)
assertDictEqual(a, b) 字典(dict)

官網還給了剩下其他的斷言,比如異常,日志等,可以查看https://docs.python.org/zh-cn/3/library/unittest.html

 

完善用例

1.用例環境清理

每個用例執行的時候需要獨特的測試環境,可以在單獨test方法中編寫,但是每個用例執行前后的環境清理或統一的預處理,需要特殊的Test Fixture方法解決

主要使用這兩種方法

  • setUp():程序會在運行每個測試用例(以 test_ 開頭的方法)之前自動執行 setUp() 方法,該方法拋出的異常都視為error,而不是測試不通過。
  • tearDown():每個測試用例(以 test_ 開頭的方法)運行完成之后自動執行 tearDown() 方法,該方法拋出的異常都視為error,而不是測試不通過,且無論用例是否出錯都會調用。
class TestStringMethods(unittest.TestCase):

    def setUp(self):
        print("test start")

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def tearDown(self):
        print("test end") 

2.用例類的環境清理

上面說的是每個測試用例(以 test_ 開頭的方法)的環境清理,那么每個測試類(繼承TestCase 的類)運行的時候怎么清理環境呢?

主要使用下面兩個類方法

  • setUpClass():一個類方法在單個類測試之前運行。setUpClass作為唯一的參數被調用時,必須使用classmethod()作為裝飾器
  • tearDownClass():一個類方法在單個類測試之后運行。setUpClass作為唯一的參數被調用時,必須使用classmethod()作為裝飾器
class TestStringMethods(unittest.TestCase):

    @classmethod
    def setUpClass(self):
        print("test start")

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    @classmethod
    def tearDownClass(self):
        print("test end") 

3.模塊級的環境清理

運行多個測試的時候,可能會將一部分功能的測試類集中在一個文件中,對這一個文件級的環境清理主要使用下面兩種方法

  • setUpModule():模塊開始時運行
  • tearDownModule():模塊結束時運行
./test.py

def setUpModule():  
    print('test module start')

def tearDownModule():  
    print("test module end")    

class Test1(unittest.TestCase):

    ...

class Test2(unittest.TestCase):

    ...

 

運行用例

1.通過代碼調用測試用例

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

2.命令行執行

#運行測試文件
python -m unittest test_module

#測試單個測試類
python -m unittest test_module.test_class

#測試多個測試類
python -m unittest test_module.test_class test_module2.test_class2

#通配符匹配測試文件執行
python -m unittest -p test*.py 

#顯示詳細信息
python -m unittest -v test_module

#幫助
python -m unittest -h

3.通過組織Test Suit后使用Test Runner運行Suite來運行測試

組織Suite的方法很多,下面怎么組織Suite在管理用例中會介紹

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

if __name__ == '__main__':
    #構建測試集
    suite = unittest.TestSuite()
    suite.addTest(TestStringMethods("test_upper"))
    #執行測試
    runner = unittest.TextTestRunner()
    runner.run(suite)

 

管理用例

通過組織TestSuite可以管理多個測試用例的執行,然后使用Test Runner運行Suite來運行測試,主要用的對象有:

  • TestSuit:組織測試用例的實例,支持測試用例的添加和刪除,最終將傳遞給 testRunner進行測試執行;
  • TextTestRunner:進行測試用例執行的實例,其中Text的意思是以文本形式顯示測試結果。測試的結果會保存到TextTestResult實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息;

1.通過addTest()的方式,上文中有

if __name__ == '__main__':
    #構建測試集
    suite = unittest.TestSuite()
    suite.addTest(TestStringMethods("test_upper"))
    #執行測試
    runner = unittest.TextTestRunner()
    runner.run(suite)

2.通過TestLoader()方式組織TestSuite

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

class TestStringMethods2(unittest.TestCase):

    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

if __name__ == '__main__':
    #此用法可以同時測試多個類
    suite1 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
    suite2 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods2) 
    suite = unittest.TestSuite([suite1, suite2]) 
    unittest.TextTestRunner().run(suite)

3.統一管理測試用例執行,比如測試用例達到成百上千個,可以將這些用例按照所測試的功能進行拆分,分散到不同的測試文件中,最后再創建用於執行所有測試用例的runtest.py文件

比如有下面很多測試文件

├─test
│      test1.py
│      test2.py
│      test3.py
│      test4.py
│      tmp1.py
│      tmp2.py
        ...

如果我們只想執行test開頭的測試文件,除了上文中的命令行命令外,我們還可以使用defaultTestLoader類提供的discover()方法來加載所有的測試用例

discover(start_dir,pattern='test*.py',top_level_dir=None)

找到指定目錄下所有測試模塊,並可遞歸查到子目錄下的測試模塊,只有匹配到文件名才能被加載。如果啟動的不是頂層目錄,那么頂層目錄必須單獨指定。

  • start_dir:要測試的模塊名或測試用例目錄路徑
  • pattern='test*.py':表示用例文件名的匹配原則。此處匹配文件名以“test”開頭的“.py”類型的文件,幸好“*”表示任意多個字符
  • top_level_dir=None:測試模塊的頂層目錄,如果沒有頂層目錄,默認為None

注意:discover()方法中的start_dir只能加載當前目錄下的.py文件,如果加載子目錄下的.py文件,需在每個子目錄下放一個_init_.py文件。

-runtest.py

import unittest

test_dir = './'
discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py')

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(discover)

當然,如果用例少,也可以使用addTest()的方式一個個添加到TestSuite中

4.跳過用例和預期失敗

unittest提供了實現某些需求的裝飾器,在執行測試用例時每個裝飾前面加@符號。

  • unittest.skip(reason):無條件的跳過裝飾的測試,說明跳過測試的原因
  • unittest.skipIf(condition,reason):跳過裝飾的測試,如果條件為真。
  • unittest.skipUnless(condition,reason):跳過裝飾的測試,除非條件為真。
  • unittest.expectedFailure():測試標記為失敗,不管執行結果是否失敗,統一標記為失敗,但不會拋出錯誤信息。
class TestStringMethods(unittest.TestCase):

    @unittest.skip("not wht")
    def test_upper(self):
        self.assertEqual('foo'.upper(), 'FOO')

5.執行順序

unittest框架默認根據ASCII碼的順序加載測試用例,數字與字母的順序為:0-9,A-Z,a-z。所以上文測試方法test_isupper()會比test_upper()先執行

同理測試類以及測試文件也是按照這個順序執行,但如果你使用addTest()的方式添加了測試,會按照添加的順序執行

 

測試結果

1.console輸出結果

結果中有幾個特殊字符表示不同的意思

  • . :代表測試通過。有幾個點就表示有幾個測試通過
  • F:代表測試失敗,F 代表 failure。
  • E:代表測試出錯,E 代表 error。
  • s:代表跳過該測試,s 代表 skip。

2.HTMLTestRunner輸出測試報告

HTMLTestRunner是一個第三方庫用於替代TestRunner,用於生成可視化的報表,是python2時期的產物,現在python3需要修改其內容才能用,不過網上有改好的,可以直接用

使用就是將下載好的HTMLTestRunner.py復制到...\python35\Lib目錄下,然后下面這樣使用

class TestStringMethods(unittest.TestCase):

    def test_upper(self):
        #HTMLTestRunner可以讀取docstring類型的注釋
        '''
        test1
        '''
        self.assertEqual('foo'.upper(), 'FOO')

    def test_isupper(self):
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

if __name__ == '__main__':

    testsuite = unittest.TestSuite()
    testsuite.addTest(TestStringMethods("test_upper"))
    testsuite.addTest(TestStringMethods("test_isupper"))
    fp = open('./result.html', 'wb')
    runner = HTMLTestRunner(stream=fp, title='測試報告', description='測試執行情況')
    runner.run(testsuite)
    fp.close()

3.如果想打印log到測試報告可以看我另一篇文章https://www.cnblogs.com/fengf233/p/10871055.html

 

 

參考:

https://docs.python.org/zh-cn/3/library/unittest.html

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM