Python UnitTest快速上手

Python UnitTest快速上手

這篇文章主要說明 Pythin  unit Testing 的寫法、

為什麼要將測試程式寫成 unit Testing? 寫成 unit test 的格式有什麼好處?

 

UnitTest 的好處

 

當我們將測試程式按照 unitTest的結構寫好之後,就可以有下列好處。

1. 測試程式的執行。可以使用 PyTest 或是 Nose這樣的工具將許多測試程式執行。特別當我們的測試程式散布各處的時候,nose這樣的工具就會自動找到這些測試程式並且執行。(檔名必須是 test_ 開頭或是 _test結尾)。除了檔名的命名之外,筆者也建議可以將測試程式及中放在 ‘test’路徑下,方便日後執行與管理。

2. 測試程式報告。nose 這樣的執行工具都會提供測試報告的輸出。我們不需要再額外開發就可以有美觀的 HTML報告。

3. unitTest 自動將每一個 function 視為一個測試個案。有利於未來測試執行與測試結果報告。建議 function 名稱命名為 test_xxxxx 開頭。如此一來,nose這樣的執行工具也很可以容易的辨識自動執行。

 

UnitTest 結構

通常一個 unitTest的測試程式結構如下,會有三大段落。

第一段落為 setUP: 這個段落主要用來準備測試資料與環境。每次測試程式執行時,只會在測試程式啟動時執行一次。

中間段落為測試個案: 這個部份就是測試程式與測試個案。每一個 function 就是一個測試個案,都會有一個對應的測試結果。

最後段落為 TearDown: 這個段落通常用來將系統資源釋放,或是將測試環境與資料復原。每次測試程式執行時只會執行一次。

[pastacode lang=”python” message=”” highlight=”” provider=”manual”]

import unittest



class test_myUnitTest(unittest.TestCase):

    def setUp(self):
        print "****setUP****"
        print "This will only run once when start-up the test program"
        print "setUP is normally used to prepare testing data/env"

    def test_case1(self):
        print "****test case 1P****"
        print "this is tesitng case 1"

    def test_case2(self):
        print "****test case 2****"
        print "this is tesitng case 2"

    def tearDown(self):
        print "****tear down****"
        print "This will only run once while closing the test program"
        print "tearDown is normally used to release/close system resource or rollback testing data"
if __name__ == '__main__':
    unittest.main()

[/pastacode]

 

如何執行?

可以使用 python

$ python   test_myUnitTest
或是如果有安裝 nose  的話,直接執行 nosetests。nose會搜尋所有 _test的檔名,執行所有 function 包含 test_的測試程式。

$nosetests

 

Assertion

測試的目的就是希望驗證測試結果的正確性。

因此 unitTest 中,會用到許多的 Assertion 來驗證測試個案的正確性。

之前提到中間斷為測試個案,每一個 function 就是一個測試個案,每一個測試個案建議對應一個 assertion

建議將每一個測試個案獨立而且最小化

 

Python程式範例

assert 各種不同的用法與範例,可以參考這個程式。

特別說明的是Fail 與 error 是不同的。

Fail 指的是測試結果實際值與預期值比對不同。測試結果失敗。

Error指的是 run-time error.通常是系統或是程式的錯誤所造成。

[pastacode lang=”python” message=”” highlight=”” provider=”manual”]

import unittest

def myfun(a,b):
    c = a + b
    raise ValueError('invalid args')


class UnitTest_1_test(unittest.TestCase):

    def test_pass(self):
        self.assertTrue(True,"AssertTue with given True")

    # Fail
    def test_fail(self):
        self.assertTrue(False,"assertTrue with given False")

    # runtime error
    def test_error(self):
        raise RuntimeError('Test error!',"raise runtimeError")

    def testEqual(self):
        self.assertEqual(2, 2)


    def testNotEqual(self):
        self.assertEqual(2, 3-2)

    def test_assert_raises_myfun(self):
        self.assertRaises(ValueError, myfun, 1, 2)


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

[/pastacode]

Assertions 常用

Method
assertTrue(x, msg=None)
assertFalse(x, msg=None)
assertIsNone(x, msg=None)
assertIsNotNone(x, msg=None)
assertEqual(a, b, msg=None)
assertNotEqual(a, b, msg=None)
assertIs(a, b, msg=None)
assertIsNot(a, b, msg=None)
assertIn(a, b, msg=None)
assertNotIn(a, b, msg=None)
assertIsInstance(a, b, msg=None)
assertNotIsInstance(a, b, msg=None)

Other Assertions

Method
assertAlmostEqual(a, b, places=7, msg=None, delta=None)
assertNotAlmostEqual(a, b, places=7, msg=None, delta=None)
assertGreater(a, b, msg=None)
assertGreaterEqual(a, b, msg=None)
assertLess(a, b, msg=None)
assertLessEqual(a, b, msg=None)
assertRegex(text, regexp, msg=None)
assertNotRegex(text, regexp, msg=None)
assertCountEqual(a, b, msg=None)
assertMultiLineEqual(a, b, msg=None)
assertSequenceEqual(a, b, msg=None)
assertListEqual(a, b, msg=None)
assertTupleEqual(a, b, msg=None)
assertSetEqual(a, b, msg=None)
assertDictEqual(a, b, msg=None)

參考資料

 

Leave a Reply

Your email address will not be published. Required fields are marked *