1. 程式人生 > 實用技巧 >unittest框架系列二(程式碼編寫與用例組織)

unittest框架系列二(程式碼編寫與用例組織)

程式碼編寫與用例組織

歡迎加入測試交流群:夜行者自動化測試(816489363)進行交流學習QAQ

–成都-阿木木


再使用unittest編寫你的測試程式碼時,測試類必須繼承TestCase或者FunctionTestCase.

  • TestCase的子類的例項是可以完全執行單個測試方法以及可選的設定和整理程式碼的物件。
  • TestCase例項的測試程式碼應完全獨立,以使它可以獨立執行或與任意數量的其他測試用例任意組合執行。

官網有一段組織測試用例的描述,為什麼會衍生出setup/teardown/setUpClass/tearDownClass,用比較直白的話來表述,就是我們不會在每一個test method中去例項化測試物件等公共操作,這些重複的操作會導致程式碼看起來很難看,所以提供了夾具test fixture來進行設定這一類需要在每個測試類或者測試方法前後進行環境初始化,或者環境清理的物件或公共操作。

當用例執行發現setup/setUpClass方法執行失敗時,不會再執行後面的test method,當teardown/tearDownClass執行失敗時,test_method方法仍然會執行。

一條測試用例是如何標記為失敗呢?使用基類assert*()提供的方法對於用例中的結果進行斷言,如果斷言失敗,就會引發異常。也就是說unittest框架通過識別異常將用例標記為fail。其他不屬於assert丟擲的異常,都會被識別成ERROR。

#!/user/bin/env python
# -*- coding: utf-8 -*-

"""
------------------------------------
@Project : mysite
@Time    : 2020/8/28 11:32
@Auth    : chineseluo
@Email   : [email protected]
@File    : unittest_demo.py
@IDE     : PyCharm
------------------------------------
"""
import unittest


class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print("運行於測試方法前,主要用於環境初始化")

    def tearDown(self):
        print("運行於測試方法後,主要使用者環境資料清理")

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

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

    def test_split(self):
        print("this is a test_split method")
        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)


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

執行結果:
運行於測試方法前,主要用於環境初始化
this is a test_isupper method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
this is a test_split method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
this is a test_upper method
運行於測試方法後,主要使用者環境資料清理
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

在下面的例子中修改test_upper方法中的self.assertEqual('foo'.upper(), 'FOO')self.assertEqual('foo'.upper(), 'FO'),檢視斷言異常執行結果,可以看到FAILED(failures=1),屬於unittest可識別的斷言異常。

import unittest


class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print("運行於測試方法前,主要用於環境初始化")

    def tearDown(self):
        print("運行於測試方法後,主要使用者環境資料清理")

    def test_upper(self):
        print("this is a test_upper method")
        self.assertEqual('foo'.upper(), 'FO')

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

    def test_split(self):
        print("this is a test_split method")
        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)


if __name__ == '__main__':
    unittest.main()
執行結果為:
C:\Users\luozhongwen\AppData\Local\Programs\Python\Python38\python.exe D:/TestScriptDir/python_web/mysite/unittest_demo.py
..F
======================================================================
FAIL: test_upper (__main__.TestStringMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:/TestScriptDir/python_web/mysite/unittest_demo.py", line 26, in test_upper
    self.assertEqual('foo'.upper(), 'FO')
AssertionError: 'FOO' != 'FO'
- FOO
?   -
+ FO


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

FAILED (failures=1)
運行於測試方法前,主要用於環境初始化
this is a test_isupper method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
this is a test_split method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
this is a test_upper method
運行於測試方法後,主要使用者環境資料清理

Process finished with exit code 1

在下面的例子的test_isupper中新增一個非斷言異常,索引越界異常,檢視執行結果,可以看到這是一個非assert的異常,被標記為了error

import unittest


class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print("運行於測試方法前,主要用於環境初始化")

    def tearDown(self):
        print("運行於測試方法後,主要使用者環境資料清理")

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

    def test_isupper(self):
        print("this is a test_isupper method")
        a = [1, 2, 3]
        print(a[4])
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test_split(self):
        print("this is a test_split method")
        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)


if __name__ == '__main__':
    unittest.main()
結果為:
======================================================================
ERROR: test_isupper (__main__.TestStringMethods)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:/TestScriptDir/python_web/mysite/unittest_demo.py", line 31, in test_isupper
    print(a[4])
IndexError: list index out of range

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

FAILED (errors=1)
運行於測試方法前,主要用於環境初始化
this is a test_isupper method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
this is a test_split method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
this is a test_upper method
運行於測試方法後,主要使用者環境資料清理

Process finished with exit code 1

執行用例:

unittest框架建議在進行測試用例編寫時,按照功能進行測試用例分組,unittest提供了一種機制:測試套件,由unittestTestSuite類表示。通常使用unittest.main(),它會自動收集所有模組的測試用例並執行它們。

有時候我們需要自定義測試套件的構建,需要自己進行測試套的新增。

  • 測試套可以放在相同的模組(例如:unittest_demo.py),針對當前測試模組,測試用例,測試方法
  • 測試套也可以放在一個指令碼的入口模組中,例如run.py中,進行各個測試模組中細分的方法的執行

放在相同指令碼的當中:

#!/user/bin/env python
# -*- coding: utf-8 -*-

"""
------------------------------------
@Project : mysite
@Time    : 2020/8/28 11:32
@Auth    : chineseluo
@Email   : [email protected]
@File    : unittest_demo.py
@IDE     : PyCharm
------------------------------------
"""
import unittest


class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print("運行於測試方法前,主要用於環境初始化")

    def tearDown(self):
        print("運行於測試方法後,主要使用者環境資料清理")

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

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

    def test_split(self):
        print("this is a test_split method")
        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 suite():
    suite = unittest.TestSuite()
    suite.addTest(TestStringMethods('test_upper'))
    suite.addTest(TestStringMethods('test_isupper'))
    return suite


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

執行結果:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
運行於測試方法前,主要用於環境初始化
this is a test_upper method
運行於測試方法後,主要使用者環境資料清理
運行於測試方法前,主要用於環境初始化
運行於測試方法後,主要使用者環境資料清理

Process finished with exit code 0

放在不同的指令碼中,通過匯入需要進行自定義測試套件的模組下的測試類:

#!/user/bin/env python
# -*- coding: utf-8 -*-

"""
------------------------------------
@Project : mysite
@Time    : 2020/8/28 11:32
@Auth    : chineseluo
@Email   : [email protected]
@File    : unittest_demo.py
@IDE     : PyCharm
------------------------------------
"""
import unittest


class TestStringMethods(unittest.TestCase):
    def setUp(self):
        print("運行於測試方法前,主要用於環境初始化")

    def tearDown(self):
        print("運行於測試方法後,主要使用者環境資料清理")

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

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

    def test_split(self):
        print("this is a test_split method")
        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)


run.py

#!/user/bin/env python
# -*- coding: utf-8 -*-

"""
------------------------------------
@Project : mysite
@Time    : 2020/8/31 9:29
@Auth    : chineseluo
@Email   : [email protected]
@File    : run.py
@IDE     : PyCharm
------------------------------------
"""
import unittest
from unittest_demo import TestStringMethods


def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestStringMethods('test_upper'))
    suite.addTest(TestStringMethods('test_isupper'))
    return suite


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