專欄地址:每週一個 Python 模塊html
unittest 是 Python 自帶的單元測試框架,能夠用來做自動化測試框架的用例組織執行。python
優勢:提供用例組織與執行方法;提供比較方法;提供豐富的日誌、清晰的報告。git
unittest 中最核心的部分是:TestFixture、TestCase、TestSuite、TestRunner。github
下面咱們分別來解釋這四個概念的意思:數據庫
run(test)
會執行 TestSuite/TestCase 中的 run(result)
方法。 測試的結果會保存到 TextTestResul t實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息。一個 class 繼承了 unittest.TestCase,即是一個測試用例,但若是其中有多個以 test
開頭的方法,那麼每有一個這樣的方法,在 load 的時候便會生成一個 TestCase 實例,如:一個 class 中有四個 test_xxx 方法,最後在 load 到 suite 中時也有四個測試用例。segmentfault
到這裏整個流程就清楚了:bash
unittest.main()
執行時,main()
會調用 TextTestRunner 中的 run()
來執行,或者能夠直接經過 TextTestRunner 來執行用例。下面咱們經過一些實例來更好地認識一下 unittest。框架
先準備待測試的方法,以下:less
# mathfunc.py
# -*- coding: utf-8 -*-
def add(a, b):
return a+b
def minus(a, b):
return a-b
def multi(a, b):
return a*b
def divide(a, b):
return a/b
複製代碼
寫 TestCase,以下:ide
# test_mathfunc.py
# -*- coding: utf-8 -*-
import unittest
from mathfunc import *
class TestMathFunc(unittest.TestCase):
"""Test mathfuc.py"""
def test_add(self):
"""Test method add(a, b)"""
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self):
"""Test method minus(a, b)"""
self.assertEqual(1, minus(3, 2))
def test_multi(self):
"""Test method multi(a, b)"""
self.assertEqual(6, multi(2, 3))
def test_divide(self):
"""Test method divide(a, b)"""
self.assertEqual(2, divide(6, 3))
self.assertEqual(2.5, divide(5, 2))
if __name__ == '__main__':
unittest.main()
複製代碼
執行結果:
.F..
======================================================================
FAIL: test_divide (__main__.TestMathFunc)
Test method divide(a, b)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:/py/test_mathfunc.py", line 26, in test_divide
self.assertEqual(2.5, divide(5, 2))
AssertionError: 2.5 != 2
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures=1)
複製代碼
可以看到一共運行了 4 個測試,失敗了 1 個,而且給出了失敗緣由,2.5 != 2
也就是說咱們的 divide 方法是有問題的。
這就是一個簡單的測試,有幾點須要說明的:
.
,失敗是 F
,出錯是 E
,跳過是 S
。從上面也能夠看出,測試的執行跟方法的順序沒有關係,test_divide 寫在了第 4 個,可是倒是第 2 個執行的。test
開頭,不然是不被 unittest 識別的。unittest.main()
中加 verbosity
參數能夠控制輸出的錯誤報告的詳細程度,默認是 1
,若是設爲 0
,則不輸出每一用例的執行結果,即沒有上面的結果中的第 1 行;若是設爲 2
,則輸出詳細的執行結果,以下:test_add (__main__.TestMathFunc)
Test method add(a, b) ... ok
test_divide (__main__.TestMathFunc)
Test method divide(a, b) ... FAIL
test_minus (__main__.TestMathFunc)
Test method minus(a, b) ... ok
test_multi (__main__.TestMathFunc)
Test method multi(a, b) ... ok
======================================================================
FAIL: test_divide (__main__.TestMathFunc)
Test method divide(a, b)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:/py/test_mathfunc.py", line 26, in test_divide
self.assertEqual(2.5, divide(5, 2))
AssertionError: 2.5 != 2
----------------------------------------------------------------------
Ran 4 tests in 0.002s
FAILED (failures=1)
複製代碼
能夠看到,每個用例的詳細執行狀況以及用例名,用例描述均被輸出了出來(在測試方法下加代碼示例中的」"」Doc String」」「,在用例執行時,會將該字符串做爲此用例的描述,加合適的註釋可以使輸出的測試報告更加便於閱讀)。
上面的代碼演示瞭如何編寫一個簡單的測試,但有兩個問題,咱們怎麼控制用例執行的順序呢?(這裏的示例中的幾個測試方法並無必定關係,但以後你寫的用例可能會有前後關係,須要先執行方法 A,再執行方法 B),咱們就要用到 TestSuite 了。咱們添加到 TestSuite 中的 case 是會按照添加的順序執行的。
問題二是咱們如今只有一個測試文件,咱們直接執行該文件便可,但若是有多個測試文件,怎麼進行組織,總不能一個個文件執行吧,答案也在 TestSuite 中。
下面來個例子:
在文件夾中咱們再新建一個文件,test_suite.py:
# -*- coding: utf-8 -*-
import unittest
from test_mathfunc import TestMathFunc
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [TestMathFunc("test_add"), TestMathFunc("test_minus"), TestMathFunc("test_divide")]
suite.addTests(tests)
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
複製代碼
執行結果:
test_add (test_mathfunc.TestMathFunc)
Test method add(a, b) ... ok
test_minus (test_mathfunc.TestMathFunc)
Test method minus(a, b) ... ok
test_divide (test_mathfunc.TestMathFunc)
Test method divide(a, b) ... FAIL
======================================================================
FAIL: test_divide (test_mathfunc.TestMathFunc)
Test method divide(a, b)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\py\test_mathfunc.py", line 26, in test_divide
self.assertEqual(2.5, divide(5, 2))
AssertionError: 2.5 != 2
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
複製代碼
能夠看到,執行狀況跟咱們預料的同樣:執行了三個 case,而且順序是按照咱們添加進 suite 的順序執行的。
上面用了 TestSuite 的 addTests()
方法,並直接傳入了 TestCase 列表,咱們還能夠:
# 直接用addTest方法添加單個TestCase
suite.addTest(TestMathFunc("test_multi"))
# 用addTests + TestLoader
# loadTestsFromName(),傳入'模塊名.TestCase名'
suite.addTests(unittest.TestLoader().loadTestsFromName('test_mathfunc.TestMathFunc'))
suite.addTests(unittest.TestLoader().loadTestsFromNames(['test_mathfunc.TestMathFunc'])) # loadTestsFromNames(),相似,傳入列表
# loadTestsFromTestCase(),傳入TestCase
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
複製代碼
注意,用 TestLoader 的方法是沒法對 case 進行排序的,同時,suite 中也能夠套 suite。
用例組織好了,但結果只能輸出到控制檯,這樣沒有辦法查看以前的執行記錄,咱們想將結果輸出到文件。很簡單,看示例:
修改 test_suite.py:
# -*- coding: utf-8 -*-
import unittest
from test_mathfunc import TestMathFunc
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
with open('UnittestTextReport.txt', 'a') as f:
runner = unittest.TextTestRunner(stream=f, verbosity=2)
runner.run(suite)
複製代碼
執行此文件,能夠看到,在同目錄下生成了 UnittestTextReport.txt,全部的執行報告均輸出到了此文件中,這下咱們便有了 txt 格式的測試報告了。
可是文本報告太過簡陋,是否是想要更加高大上的 HTML 報告?但 unittest 本身可沒有帶 HTML 報告,咱們只能求助於外部的庫了。
HTMLTestRunner 是一個第三方的 unittest HTML 報告庫,咱們下載 HTMLTestRunner.py,並導入就能夠運行了。
官方地址:tungwaiyip.info/software/HT…
修改咱們的 test_suite.py
:
# -*- coding: utf-8 -*-
import unittest
from test_mathfunc import TestMathFunc
from HTMLTestRunner import HTMLTestRunner
if __name__ == '__main__':
suite = unittest.TestSuite()
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc))
with open('HTMLReport.html', 'w') as f:
runner = HTMLTestRunner(stream=f,
title='MathFunc Test Report',
description='generated by HTMLTestRunner.',
verbosity=2
)
runner.run(suite)
複製代碼
這樣,在執行時,在控制檯咱們可以看到執行狀況,以下:
ok test_add (test_mathfunc.TestMathFunc)
F test_divide (test_mathfunc.TestMathFunc)
ok test_minus (test_mathfunc.TestMathFunc)
ok test_multi (test_mathfunc.TestMathFunc)
Time Elapsed: 0:00:00.001000
複製代碼
而且輸出了 HTML 測試報告,HTMLReport.html
。
這下漂亮的 HTML 報告也有了。其實你能發現,HTMLTestRunner 的執行方法跟 TextTestRunner 很類似,你能夠跟上面的示例對比一下,就是把類圖中的 runner 換成了 HTMLTestRunner,並將 TestResult 用 HTML 的形式展示出來,若是你研究夠深,能夠寫本身的 runner,生成更復雜更漂亮的報告。
上面整個測試基本跑了下來,但可能會遇到點特殊的狀況:若是個人測試須要在每次執行以前準備環境,或者在每次執行完以後須要進行一些清理怎麼辦?好比執行前須要鏈接數據庫,執行完成以後須要還原數據、斷開鏈接。總不能每一個測試方法中都添加準備環境、清理環境的代碼吧。
這就要涉及到咱們以前說過的 test fixture 了,修改 test_mathfunc.py:
# -*- coding: utf-8 -*-
import unittest
from mathfunc import *
class TestMathFunc(unittest.TestCase):
"""Test mathfuc.py"""
def setUp(self):
print "do something before test.Prepare environment."
def tearDown(self):
print "do something after test.Clean up."
def test_add(self):
"""Test method add(a, b)"""
print "add"
self.assertEqual(3, add(1, 2))
self.assertNotEqual(3, add(2, 2))
def test_minus(self):
"""Test method minus(a, b)"""
print "minus"
self.assertEqual(1, minus(3, 2))
def test_multi(self):
"""Test method multi(a, b)"""
print "multi"
self.assertEqual(6, multi(2, 3))
def test_divide(self):
"""Test method divide(a, b)"""
print "divide"
self.assertEqual(2, divide(6, 3))
self.assertEqual(2.5, divide(5, 2))
複製代碼
咱們添加了 setUp()
和 tearDown()
兩個方法(實際上是重寫了 TestCase 的這兩個方法),這兩個方法在每一個測試方法執行前以及執行後執行一次,setUp 用來爲測試準備環境,tearDown 用來清理環境,已備以後的測試。
咱們再執行一次:
test_add (test_mathfunc.TestMathFunc)
Test method add(a, b) ... ok
test_divide (test_mathfunc.TestMathFunc)
Test method divide(a, b) ... FAIL
test_minus (test_mathfunc.TestMathFunc)
Test method minus(a, b) ... ok
test_multi (test_mathfunc.TestMathFunc)
Test method multi(a, b) ... ok
======================================================================
FAIL: test_divide (test_mathfunc.TestMathFunc)
Test method divide(a, b)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\py\test_mathfunc.py", line 36, in test_divide
self.assertEqual(2.5, divide(5, 2))
AssertionError: 2.5 != 2
----------------------------------------------------------------------
Ran 4 tests in 0.000s
FAILED (failures=1)
do something before test.Prepare environment.
add
do something after test.Clean up.
do something before test.Prepare environment.
divide
do something after test.Clean up.
do something before test.Prepare environment.
minus
do something after test.Clean up.
do something before test.Prepare environment.
multi
do something after test.Clean up.
複製代碼
能夠看到 setUp 和 tearDown 在每次執行 case 先後都執行了一次。
若是想要在全部 case 執行以前準備一次環境,並在全部 case 執行結束以後再清理環境,咱們能夠用 setUpClass()
與 tearDownClass()
:
...
class TestMathFunc(unittest.TestCase):
"""Test mathfuc.py"""
@classmethod
def setUpClass(cls):
print "This setUpClass() method only called once."
@classmethod
def tearDownClass(cls):
print "This tearDownClass() method only called once too."
...
複製代碼
執行結果以下:
...
This setUpClass() method only called once.
do something before test.Prepare environment.
add
...
multi
do something after test.Clean up.
This tearDownClass() method only called once too.
複製代碼
能夠看到 setUpClass 以及 tearDownClass 均只執行了一次。
大多數測試斷言某些條件的真實性。編寫真值檢查測試有兩種不一樣的方法,具體取決於測試做者的觀點以及所測試代碼的預期結果。
# unittest_truth.py
import unittest
class TruthTest(unittest.TestCase):
def testAssertTrue(self):
self.assertTrue(True)
def testAssertFalse(self):
self.assertFalse(False)
複製代碼
若是代碼生成的值爲 true,則應使用assertTrue()
方法。若是代碼產生值爲 false,則方法assertFalse()
更有意義。
$ python3 -m unittest -v unittest_truth.py
testAssertFalse (unittest_truth.TruthTest) ... ok
testAssertTrue (unittest_truth.TruthTest) ... ok
----------------------------------------------------------------
Ran 2 tests in 0.000s
OK
複製代碼
unittest
包括測試兩個值相等的方法以下:
# unittest_equality.py
import unittest
class EqualityTest(unittest.TestCase):
def testExpectEqual(self):
self.assertEqual(1, 3 - 2)
def testExpectEqualFails(self):
self.assertEqual(2, 3 - 2)
def testExpectNotEqual(self):
self.assertNotEqual(2, 3 - 2)
def testExpectNotEqualFails(self):
self.assertNotEqual(1, 3 - 2)
複製代碼
當失敗時,這些特殊的測試方法會產生錯誤消息,包括被比較的值。
$ python3 -m unittest -v unittest_equality.py
testExpectEqual (unittest_equality.EqualityTest) ... ok
testExpectEqualFails (unittest_equality.EqualityTest) ... FAIL
testExpectNotEqual (unittest_equality.EqualityTest) ... ok
testExpectNotEqualFails (unittest_equality.EqualityTest) ... FAIL
================================================================
FAIL: testExpectEqualFails (unittest_equality.EqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality.py", line 15, in
testExpectEqualFails
self.assertEqual(2, 3 - 2)
AssertionError: 2 != 1
================================================================
FAIL: testExpectNotEqualFails (unittest_equality.EqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality.py", line 21, in
testExpectNotEqualFails
self.assertNotEqual(1, 3 - 2)
AssertionError: 1 == 1
----------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=2)
複製代碼
除了嚴格相等以外,還可使用assertAlmostEqual()
和assertNotAlmostEqual()
測試浮點數的近似相等。
# unittest_almostequal.py
import unittest
class AlmostEqualTest(unittest.TestCase):
def testEqual(self):
self.assertEqual(1.1, 3.3 - 2.2)
def testAlmostEqual(self):
self.assertAlmostEqual(1.1, 3.3 - 2.2, places=1)
def testNotAlmostEqual(self):
self.assertNotAlmostEqual(1.1, 3.3 - 2.0, places=1)
複製代碼
參數是要比較的值,以及用於測試的小數位數。
$ python3 -m unittest unittest_almostequal.py
.F.
================================================================
FAIL: testEqual (unittest_almostequal.AlmostEqualTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_almostequal.py", line 12, in testEqual
self.assertEqual(1.1, 3.3 - 2.2)
AssertionError: 1.1 != 1.0999999999999996
----------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
複製代碼
除了通用的assertEqual()
和 assertNotEqual()
,也有比較list
,dict
和set
對象的方法。
# unittest_equality_container.py
import textwrap
import unittest
class ContainerEqualityTest(unittest.TestCase):
def testCount(self):
self.assertCountEqual(
[1, 2, 3, 2],
[1, 3, 2, 3],
)
def testDict(self):
self.assertDictEqual(
{'a': 1, 'b': 2},
{'a': 1, 'b': 3},
)
def testList(self):
self.assertListEqual(
[1, 2, 3],
[1, 3, 2],
)
def testMultiLineString(self):
self.assertMultiLineEqual(
textwrap.dedent(""" This string has more than one line. """),
textwrap.dedent(""" This string has more than two lines. """),
)
def testSequence(self):
self.assertSequenceEqual(
[1, 2, 3],
[1, 3, 2],
)
def testSet(self):
self.assertSetEqual(
set([1, 2, 3]),
set([1, 3, 2, 4]),
)
def testTuple(self):
self.assertTupleEqual(
(1, 'a'),
(1, 'b'),
)
複製代碼
每種方法都使用對輸入類型有意義的格式定義函數,使測試失敗更容易理解和糾正。
$ python3 -m unittest unittest_equality_container.py
FFFFFFF
================================================================
FAIL: testCount
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 15, in
testCount
[1, 3, 2, 3],
AssertionError: Element counts were not equal:
First has 2, Second has 1: 2
First has 1, Second has 2: 3
================================================================
FAIL: testDict
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 21, in
testDict
{'a': 1, 'b': 3},
AssertionError: {'a': 1, 'b': 2} != {'a': 1, 'b': 3}
- {'a': 1, 'b': 2}
? ^
+ {'a': 1, 'b': 3}
? ^
================================================================
FAIL: testList
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 27, in
testList
[1, 3, 2],
AssertionError: Lists differ: [1, 2, 3] != [1, 3, 2]
First differing element 1:
2
3
- [1, 2, 3]
+ [1, 3, 2]
================================================================
FAIL: testMultiLineString
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 41, in
testMultiLineString
"""),
AssertionError: '\nThis string\nhas more than one\nline.\n' !=
'\nThis string has\nmore than two\nlines.\n'
- This string
+ This string has
? ++++
- has more than one
? ---- --
+ more than two
? ++
- line.
+ lines.
? +
================================================================
FAIL: testSequence
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 47, in
testSequence
[1, 3, 2],
AssertionError: Sequences differ: [1, 2, 3] != [1, 3, 2]
First differing element 1:
2
3
- [1, 2, 3]
+ [1, 3, 2]
================================================================
FAIL: testSet
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 53, in testSet
set([1, 3, 2, 4]),
AssertionError: Items in the second set but not the first:
4
================================================================
FAIL: testTuple
(unittest_equality_container.ContainerEqualityTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_equality_container.py", line 59, in
testTuple
(1, 'b'),
AssertionError: Tuples differ: (1, 'a') != (1, 'b')
First differing element 1:
'a'
'b'
- (1, 'a')
? ^
+ (1, 'b')
? ^
----------------------------------------------------------------
Ran 7 tests in 0.005s
FAILED (failures=7)
複製代碼
使用assertIn()
測試容器關係。
# unittest_in.py
import unittest
class ContainerMembershipTest(unittest.TestCase):
def testDict(self):
self.assertIn(4, {1: 'a', 2: 'b', 3: 'c'})
def testList(self):
self.assertIn(4, [1, 2, 3])
def testSet(self):
self.assertIn(4, set([1, 2, 3]))
複製代碼
任何對象都支持in
運算符或容器 API assertIn()
。
$ python3 -m unittest unittest_in.py
FFF
================================================================
FAIL: testDict (unittest_in.ContainerMembershipTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_in.py", line 12, in testDict
self.assertIn(4, {1: 'a', 2: 'b', 3: 'c'})
AssertionError: 4 not found in {1: 'a', 2: 'b', 3: 'c'}
================================================================
FAIL: testList (unittest_in.ContainerMembershipTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_in.py", line 15, in testList
self.assertIn(4, [1, 2, 3])
AssertionError: 4 not found in [1, 2, 3]
================================================================
FAIL: testSet (unittest_in.ContainerMembershipTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_in.py", line 18, in testSet
self.assertIn(4, set([1, 2, 3]))
AssertionError: 4 not found in {1, 2, 3}
----------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=3)
複製代碼
如前所述,若是測試引起異常,則將 AssertionError
視爲錯誤。這對於修改具備現有測試覆蓋率的代碼時發現錯誤很是有用。可是,在某些狀況下,測試應驗證某些代碼是否確實產生異常。
例如,若是給對象的屬性賦予無效值。在這種狀況下, assertRaises()
使代碼比在測試中捕獲異常更清晰。比較這兩個測試:
# unittest_exception.py
import unittest
def raises_error(*args, **kwds):
raise ValueError('Invalid value: ' + str(args) + str(kwds))
class ExceptionTest(unittest.TestCase):
def testTrapLocally(self):
try:
raises_error('a', b='c')
except ValueError:
pass
else:
self.fail('Did not see ValueError')
def testAssertRaises(self):
self.assertRaises(
ValueError,
raises_error,
'a',
b='c',
)
複製代碼
二者的結果是相同的,但第二次使用的 assertRaises()
更簡潔。
$ python3 -m unittest -v unittest_exception.py
testAssertRaises (unittest_exception.ExceptionTest) ... ok
testTrapLocally (unittest_exception.ExceptionTest) ... ok
----------------------------------------------------------------
Ran 2 tests in 0.000s
OK
複製代碼
使用不一樣的輸入運行相同的測試邏輯一般頗有用。不是爲每一個小案例定義單獨的測試方法,而是使用一種包含多個相關斷言調用的測試方法。這種方法的問題在於,只要一個斷言失敗,就會跳過其他的斷言。更好的解決方案是使用subTest()
在測試方法中爲測試建立上下文。若是測試失敗,則報告失敗並繼續進行其他測試。
# unittest_subtest.py
import unittest
class SubTest(unittest.TestCase):
def test_combined(self):
self.assertRegex('abc', 'a')
self.assertRegex('abc', 'B')
# The next assertions are not verified!
self.assertRegex('abc', 'c')
self.assertRegex('abc', 'd')
def test_with_subtest(self):
for pat in ['a', 'B', 'c', 'd']:
with self.subTest(pattern=pat):
self.assertRegex('abc', pat)
複製代碼
在該示例中,test_combined()
方法從不運行斷言'c'
和'd'
。test_with_subtest()
方法能夠正確報告其餘故障。請注意,即便報告了三個故障,測試運行器仍然認爲只有兩個測試用例。
$ python3 -m unittest -v unittest_subtest.py
test_combined (unittest_subtest.SubTest) ... FAIL
test_with_subtest (unittest_subtest.SubTest) ...
================================================================
FAIL: test_combined (unittest_subtest.SubTest)
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_subtest.py", line 13, in test_combined
self.assertRegex('abc', 'B')
AssertionError: Regex didn't match: 'B' not found in 'abc'
================================================================
FAIL: test_with_subtest (unittest_subtest.SubTest) (pattern='B')
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_subtest.py", line 21, in test_with_subtest
self.assertRegex('abc', pat)
AssertionError: Regex didn't match: 'B' not found in 'abc'
================================================================
FAIL: test_with_subtest (unittest_subtest.SubTest) (pattern='d')
----------------------------------------------------------------
Traceback (most recent call last):
File ".../unittest_subtest.py", line 21, in test_with_subtest
self.assertRegex('abc', pat)
AssertionError: Regex didn't match: 'd' not found in 'abc'
----------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=3)
複製代碼
若是咱們臨時想要跳過某個 case 不執行怎麼辦?unittest 也提供了幾種方法:
...
class TestMathFunc(unittest.TestCase):
"""Test mathfuc.py"""
...
@unittest.skip("I don't want to run this case.")
def test_divide(self):
"""Test method divide(a, b)"""
print "divide"
self.assertEqual(2, divide(6, 3))
self.assertEqual(2.5, divide(5, 2))
複製代碼
執行:
...
test_add (test_mathfunc.TestMathFunc)
Test method add(a, b) ... ok
test_divide (test_mathfunc.TestMathFunc)
Test method divide(a, b) ... skipped "I don't want to run this case."
test_minus (test_mathfunc.TestMathFunc)
Test method minus(a, b) ... ok
test_multi (test_mathfunc.TestMathFunc)
Test method multi(a, b) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK (skipped=1)
複製代碼
能夠看到總的 test 數量仍是 4 個,但 divide() 方法被 skip 了。
skip 裝飾器一共有三個 unittest.skip(reason)
、unittest.skipIf(condition, reason)
、unittest.skipUnless(condition, reason)
,skip 無條件跳過,skipIf 當 condition 爲 True 時跳過,skipUnless 當 condition 爲 False 時跳過。
...
class TestMathFunc(unittest.TestCase):
"""Test mathfuc.py"""
...
def test_divide(self):
"""Test method divide(a, b)"""
self.skipTest('Do not run this.')
print "divide"
self.assertEqual(2, divide(6, 3))
self.assertEqual(2.5, divide(5, 2))
複製代碼
輸出:
...
test_add (test_mathfunc.TestMathFunc)
Test method add(a, b) ... ok
test_divide (test_mathfunc.TestMathFunc)
Test method divide(a, b) ... skipped 'Do not run this.'
test_minus (test_mathfunc.TestMathFunc)
Test method minus(a, b) ... ok
test_multi (test_mathfunc.TestMathFunc)
Test method multi(a, b) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK (skipped=1)
複製代碼
效果跟上面的裝飾器同樣,跳過了 divide 方法。
可使用expectedFailure()
裝飾器來忽略失敗的測試。
# unittest_expectedfailure.py
import unittest
class Test(unittest.TestCase):
@unittest.expectedFailure
def test_never_passes(self):
self.assertTrue(False)
@unittest.expectedFailure
def test_always_passes(self):
self.assertTrue(True)
複製代碼
若是預期失敗的測試經過了,則該條件被視爲特殊類型的失敗,並報告爲「意外成功」。
$ python3 -m unittest -v unittest_expectedfailure.py
test_always_passes (unittest_expectedfailure.Test) ...
unexpected success
test_never_passes (unittest_expectedfailure.Test) ... expected
failure
----------------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (expected failures=1, unexpected successes=1)
複製代碼
test
開頭的方法在 load 時被加載爲一個真正的 TestCase。0
是簡單報告、1
是通常報告、2
是詳細報告。setUp()
、tearDown()
、setUpClass()
以及 tearDownClass()
能夠在用例執行前佈置環境,以及在用例執行後清理環境。
相關文檔: