Unittest入門與實作 – Python為例
這篇文章主要針對 unitTest的基本概念做介紹,並且利用 Python unitTest為實作。
其實每個程式語言都有提供對應的 unitTest 程式庫,例如 Java 提供 JUnit 或是TestNG。
除了語法上的差異之外,主要的觀念與要達到的目的都相同,
那就是將程式拆解為單元unit,進行驗證assert,產出測試結果。
為什麼要使用 UnitTest
不管是Python 的unitTest 或是Java Junit or Java testNG這些unitTesting 的架構通常提供下列功能:
- 測試個案與測試資料的管理與設定
- 測試個案執行時的設定,例如:哪些測試個案要執行測試,哪些不要。
- 測試群組,許多的測試個案可以組合成一個測試群組
- 測試報告結果。每個測試個案的執行結果
- 驗證assertion。assertEqual, assertTrue等。assert也是 unitTest 最重要的核心,提供驗證的功能,並且將該成功或是失敗的結果記錄在測試結果報告。每一個測試個案都一定會有 assertion。通常測試結果有三種,ok, fail, error.
一定要用 unitTest 才可以做測試的驗證嗎? 其實不一定。但是使用 unitTest 架構會讓整個驗證的工作變得更容易。
舉個小例子,程式如果要驗證 a = 10,如果不用 UnitTest架構的話,程式驗證可能會寫成
[pastacode lang=”javascript” message=”” highlight=”” provider=”manual”]
if a = 10
print "testing success"
else
print "testing failure"
[/pastacode]
但是,透過 unitTest架構的幫忙,我們只需要寫 assertEqual(a, 10)就好。
沒錯,UnitTest 會根據該 assert成功與失敗自動記錄測試結果,最後將該測試結果輸出到測試報告。是不是很方便?
第一個 UnitTest測試程式
[pastacode lang=”python” message=”” highlight=”” provider=”manual”]
import unittest
class SimplisticTest(unittest.TestCase):
def test(self):
self.assertTrue("hello"=="hello")
if __name__ == '__main__':
unittest.main()
[/pastacode]
每一個 unitTest的基本架構都是由許多 test method/function 組成,
每個 test method/function就是一個測試個案,
因此每個測試個案都會有需要驗證的assert
整個測試程式執行透過
if __name__ == '__main__': unittest.main()
執行測試程式
透過指令列,執行該 python script 就會進行測試,並且列印出測試結果
$ python my_1st_unitTest.py
"-v" 可以列印出更多測試過程中的資訊。
$ python3
my_1st_unitTest.py -v
測試結果有三種情況
測試結果 | 意義 | |
ok | 測試結果成功。 | |
fail | 測試結果失敗。 | |
error |
這個通常是執行過程中發生的非預期錯誤。 RuntimeError
|
[pastacode lang=”python” message=”” highlight=”” provider=”manual”]
import unittest
class OutcomesTest(unittest.TestCase):
def test_pass(self):
self.assertTrue(True)
def test_fail(self):
self.assertTrue(False)
def test_error(self):
raise RuntimeError('Test error!')
if __name__ == '__main__':
unittest.main()
[/pastacode]
Assertion
我們可以利用 assertion 做很多不同情況的驗證
Method | Checks that | New in |
---|---|---|
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 | 3.1 |
assertIsNot(a, b) | a is not b | 3.1 |
assertIsNone(x) | x is None | 3.1 |
assertIsNotNone(x) | x is not None | 3.1 |
assertIn(a, b) | a in b | 3.1 |
assertNotIn(a, b) | a not in b | 3.1 |
assertIsInstance(a, b) | isinstance(a, b) | 3.2 |
assertNotIsInstance(a, b) | not isinstance(a, b) | 3.2 |
例外與錯誤
如果有預期的錯誤,也可以用 assert驗證錯誤是否有預期的發生並且處理。
Method | Checks that | New in |
---|---|---|
assertRaises(exc, fun, *args, **kwds) | fun(*args, **kwds) raises exc | |
assertRaisesRegex(exc, r, fun, *args,**kwds) | fun(*args, **kwds) raises excand the message matches regex r | 3.1 |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds) raises warn | 3.2 |
assertWarnsRegex(warn, r, fun, *args,**kwds) | fun(*args, **kwds) raises warnand the message matches regex r | 3.2 |
https://docs.python.org/3.3/library/unittest.html#assert-methods
舉例來說,如果我們預期會有除以零ZeroDivisionError的錯誤,那麼就可以利用 assertRaises來處理
[pastacode lang=”python” message=”” highlight=”” provider=”manual”]
import unittest
def myFun(a, b):
print a, b
c = a/b
class ExceptionTest(unittest.TestCase):
def test_assert_raises(self):
self.assertRaises(ZeroDivisionError, myFun, 20,0)
if __name__ == '__main__':
unittest.main()
[/pastacode]
測試資料與環境的準備
測試個案所需要用到的資料或是環境又稱為Test Fixtures,
UnitTest的程式中,我們主要透過 setUp與 tearDown 來處理,
setUp主要用在每次測試個案執行前,一定會執行的動作
tearDown則是測試個案執行完畢後,需要執行的動作。
- def setUp(self):
- def tearDown(self):
[pastacode lang=”python” message=”” highlight=”” provider=”manual”]
import unittest
class FixturesTest(unittest.TestCase):
def setUp(self):
print('In setUp()')
def tearDown(self):
print('In tearDown()')
def test(self):
print('in test()')
if __name__ == '__main__':
unittest.main()
[/pastacode]
例如上面這個範例,程式執行後,執行結果為:
In setUp()
in test()
In tearDown()
測試個案的執行
當我們有許多的 python檔案,許多的測試個案,要如何有效的管理,
例如:這次的測試執行只執行某一類型的測試個案,而不是全部執行
可以透過 unitTest提供的 test suite 來做測試個案執行的管理
另外,筆者推薦 nosetests,當有許多的測試個案檔案的蛇後 noseTests就很方便
因為,noseTests可以用來執行所指定的的測試個案,
nosetests only_test_this.py nosetests test.module
nosetests another.test:TestCase.test_method nosetests a.test:TestCase nosetests /path/to/test/file.py:test_function
nose 參考資料 http://nose.readthedocs.org/en/latest/usage.html#selecting-tests
小結
這篇文章我們簡介 unitTest的基本觀念
我們用 python舉幾個 unitTest的用法,介紹 Assertion
最後,對於測試個案的執行建議另外一個模組 nose
善用 unitTest可以讓整個測試驗證工作更加容易並且提早找出重大瑕疵讓品質提升。