一. unittest
unittest 和 JUnit類似,可以說是python的標(biāo)準單元測試框架,所以有時也被人稱為 PyUnit。它使用起來和xUnit 家族其他成員類似。 用的人也比較多。兼容 python2 以及python3 。
個人比較喜歡用這個,主要之前用過JUnit,用這個上手就很快。而且屬于python自動集成,不用額外的安裝包,感覺是該有的都有了,用著方便。
官網(wǎng)示例:
按 Ctrl+C 復(fù)制代碼
import unittest
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())
def test_split(self): 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()
二. unittest2
unittest2 可以說是一個針對 unittest 測試框架新特性的補丁。它很大程度上和 unittest 都類似。然后還添加了一些 unittest 沒有的方法。
三. pytest
看了一下,pytest 文檔還是蠻詳細的。比較關(guān)注的一點是,pytest 直接可以通過 @pytest.mark.parametrize 進行參數(shù)化,而 unittest 則需要借助 DDT。
官網(wǎng)示例:
# content of test_sample.py
def inc(x):
return x + 1
?
def test_answer():
assert inc(3) == 5
執(zhí)行如下:
$ pytest
======= test session starts ========
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 item
?
test_sample.py F
?
======= FAILURES ========
_______ test_answer ________
?
def test_answer():
> assert inc(3) == 5
E assert 4 == 5
E + where 4 = inc(3)
?
test_sample.py:5: AssertionError
======= 1 failed in 0.12 seconds ========
四. nose
nose 擴展了 unittest,從而使得測試更容易。
一般可以用 unittest 方式寫用例,寫完之后用 nose 來執(zhí)行。nose 的測試收集方式還是很方便的。
還有一個特定就是,nose 可以采用 @with_setup() 來定義方法的 setup 和 teardown。
官方示例:
def setup_func():
"set up test fixtures"
?
def teardown_func():
"tear down test fixtures"
?
@with_setup(setup_func, teardown_func)
def test():
"test ..."
五. doctest
doctest 模塊會搜索那些看起來像交互式會話的 Python 代碼片段,然后嘗試執(zhí)行并驗證結(jié)果。
doctest 中,如果要寫測試用例,只需要在寫在以 ''' '''包圍的文檔注釋即可,也就是可以被 doc 這個屬性引用到的地方。這點比較特別,跟其他單元測試框架都不一樣。但是我覺得這樣的話就注定了 doctest 不適合大型測試,因為做不到代碼和測試的分離。
import doctest
?
"""
This is the "example" module.
?
The example module supplies one function, factorial(). For example,
?
>>> factorial(5)
120
"""
?
def factorial(n):
"""Return the factorial of n, an exact integer >= 0.
?
>>> [factorial(n) for n in range(6)]
[1, 1, 2, 6, 24, 120]
>>> factorial(30)
265252859812191058636308480000000
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: n must be >= 0
?
Factorials of floats are OK, but the float must be an exact integer:
>>> factorial(30.1)
Traceback (most recent call last):
...
ValueError: n must be exact integer
>>> factorial(30.0)
265252859812191058636308480000000
?
It must also not be ridiculously large:
>>> factorial(1e100)
Traceback (most recent call last):
...
OverflowError: n too large
"""
?
import math
if not n >= 0:
raise ValueError("n must be >= 0")
if math.floor(n) != n:
raise ValueError("n must be exact integer")
if n+1 == n: # catch a value like 1e300
raise OverflowError("n too large")
result = 1
factor = 2
while factor <= n:
result *= factor
factor += 1
return result
?
?
if __name__ == "__main__":
doctest.testmod(verbose=True)
verbose 參數(shù)用于控制是否輸出詳細信息,默認為 False ,如果不寫,那么運行時不會輸出任何東西,除非測試 fail。
輸出如下:
Trying:
[factorial(n) for n in range(6)]
Expecting:
[1, 1, 2, 6, 24, 120]
ok
Trying:
factorial(30)
Expecting:
265252859812191058636308480000000
ok
Trying:
factorial(-1)
Expecting:
Traceback (most recent call last):
...
ValueError: n must be >= 0
ok
Trying:
factorial(30.1)
Expecting:
Traceback (most recent call last):
...
ValueError: n must be exact integer
ok
Trying:
factorial(30.0)
Expecting:
265252859812191058636308480000000
ok
Trying:
factorial(1e100)
Expecting:
Traceback (most recent call last):
...
OverflowError: n too large
ok
1 items had no tests:
__main__
1 items passed all tests:
6 tests in __main__.factorial
6 tests in 2 items.
6 passed and 0 failed.
Test passed.