assert
assertを使ってみます。
a = 0 for i in range(5): a += i assert a < 30, a print a a = 0 for i in range(10): a += i assert a < 30, a print a
実行結果は以下のようになります。
10
Traceback (most recent call last):
File "a.py", line 10, in ?
assert a < 30, a
AssertionError: 36assertは
assert 式1, 式2
のように使います。式1が真の場合は何もしないのですが、式1が偽の場合は例外AssertionErrorを送出し、式2を評価します。実行結果を見ると最初のループは a < 30 が真のまま終了し、print a が実行されていることがわかります。二番目のループでは途中で a < 30 が偽となり、AssertionErrorが送出されています。また、そのときの a が評価され、36 であることがわかります。
unittest.TestCase
unittestをするためにはunittest.TestCaseのサブクラスを作り、testで始まるメソッドを定義します。メソッドの中でassert*を使い、assert*による例外が発生しなかった場合、テストの結果は成功になります。
参考資料
- http://www.python.jp/doc/2.4/lib/node161.html
- http://www.python.jp/doc/2.4/lib/testcase-objects.html
# test.py
import unittest
class FooTest(unittest.TestCase):
def setUp(self):
self.seq = [i for i in range(10) if i%2]
def test_str(self):
"""''.join(map(str, self.seq)) == '13579'?"""
self.assertEqual(''.join(map(str, self.seq)), '13579')
def test_num(self):
'''reduce(lambda x, y: x*y, self.seq) < 500?'''
a = reduce(lambda x, y: x*y, self.seq)
self.assert_(a < 500, a)
def test_seq(self):
'''1 in [1, 3, 5, 7, 9]?'''
self.assert_(1 in self.seq)
def test_almosteq(self):
'''4.989 is almost_eq 4.99?'''
self.assertAlmostEqual(4.989, 4.99, 2)
def test_raises(self):
'''test_raises'''
self.assertRaises(TypeError, self.seq, 1)
def test_IndexError(self):
'''raise IndexError'''
print self.seq[100]
unittest.main()実行結果は以下のようになります。
> python test.py
E.F...
======================================================================
ERROR: raise IndexError
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 31, in test_IndexError
print self.seq[100]
IndexError: list index out of range
======================================================================
FAIL: reduce(lambda x, y: x*y, self.seq) < 500?
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 15, in test_num
self.assert_(a < 500, a)
AssertionError: 945
----------------------------------------------------------------------
Ran 6 tests in 0.003s
FAILED (failures=1, errors=1)最初の E.F... は、テストを六つ行い、四つが成功(.)、一つが失敗(F)、一つがassert以外の例外によるエラー(E)であることを示しています。
テストの結果を細かく見る場合は -v をつけて実行します。
> python test.py -v
raise IndexError ... ERROR
4.989 is almost_eq 4.99? ... ok
reduce(lambda x, y: x*y, self.seq) < 500? ... FAIL
test_raises ... ok
1 in [1, 3, 5, 7, 9]? ... ok
''.join(map(str, self.seq)) == '13579'? ... ok
======================================================================
ERROR: raise IndexError
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 31, in test_IndexError
print self.seq[100]
IndexError: list index out of range
======================================================================
FAIL: reduce(lambda x, y: x*y, self.seq) < 500?
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 15, in test_num
self.assert_(a < 500, a)
AssertionError: 945
----------------------------------------------------------------------
Ran 6 tests in 0.011s
FAILED (failures=1, errors=1)unittes.mainは引数moduleで渡したモジュール中で定義されているTestCase(のサブクラス)を全て実行してくれるみたいです。何も渡さない場合は'__main__'で定義されているテストを全て実行します。
# b.py
import unittest
import random
class BarTest(unittest.TestCase):
def setUp(self):
self.seq = range(random.randint(1, 1000))
def test_len(self):
'''len(self.seq) < 100?'''
self.assert_(len(self.seq) < 100, len(self.seq))# test.py
import unittest
import b
class FooTest(unittest.TestCase):
def setUp(self):
self.seq = [i for i in range(10) if i%2]
def test_num(self):
'''reduce(lambda x, y: x*y, self.seq) < 500?'''
a = reduce(lambda x, y: x*y, self.seq)
self.assert_(a < 500, a)
unittest.main(module=b)というファイルがあった場合、test.pyを実行するとb.pyで定義されているテストが実行されます。unittest.mainの引数でmoduleが指定されていない場合はtest.pyで定義されているテストが実行されます。
unittest.TestSuite
いくつかのテストをまとめて実行する場合はunittest.TestSuiteを使います。
# b.py
import unittest
import random
class BarTest(unittest.TestCase):
def setUp(self):
self.seq = range(random.randint(1, 1000))
def test_len(self):
'''len(self.seq) < 100?'''
self.assert_(len(self.seq) < 100, len(self.seq))# test.py
import unittest
import b
class FooTest(unittest.TestCase):
def setUp(self):
self.seq = [i for i in range(10) if i%2]
def test_num(self):
'''reduce(lambda x, y: x*y, self.seq) < 500?'''
a = reduce(lambda x, y: x*y, self.seq)
self.assert_(a < 500, a)
def test_seq(self):
'''1 in [1, 3, 5, 7, 9]?'''
self.assert_(1 in self.seq)
suite = unittest.TestSuite()
#suite.addTest(Footest('test_num')) # <= suiteにFooTestのtest_numだけを追加
suite.addTest(unittest.makeSuite(FooTest)) # <= suiteにFooTestで定義されたテストを全て追加
suite.addTest(unittest.makeSuite(b.BarTest)) # <= suiteにBarTestで定義されたテストを全て追加
unittest.TextTestRunner(verbosity=2).run(suite) # <= suiteに追加されたTestCaseを全て実行して結果を表示TestCaseのサブクラス中で定義された全てのテストをsuiteに追加するためにはunittest.makeSuiteでTestSuiteに変換します。
実行結果は以下のようになります。
reduce(lambda x, y: x*y, self.seq) < 500? ... FAIL
1 in [1, 3, 5, 7, 9]? ... ok
len(self.seq) < 100? ... ok
======================================================================
FAIL: reduce(lambda x, y: x*y, self.seq) < 500?
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 11, in test_num
self.assert_(a < 500, a)
AssertionError: 945
----------------------------------------------------------------------
Ran 3 tests in 0.008s
FAILED (failures=1)unittest.TextTestRunner(verbosity=2).run(suite)のverbosityを変えることで結果の表示の細かさを変えることができます。
unittest.FunctionTestCase
unittest.FunctionTestCaseを使うと、関数をTestCaseに変換することができます。
# test.py
import unittest
def func_test():
'''func_test'''
a = 0
for i in range(10):
a += i
assert a > 30, a
suite = unittest.TestSuite()
suite.addTest(unittest.FunctionTestCase(func_test)) # <= 関数からTestCaseに変換してsuiteに追加
unittest.TextTestRunner(verbosity=2).run(suite) # <= suiteに追加されたTestCaseを全て実行して結果を表示実行結果は以下のようになります。
func_test ... FAIL
======================================================================
FAIL: func_test
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 9, in func_test
assert a < 30, a
AssertionError: 36
----------------------------------------------------------------------
Ran 1 test in 0.004s
FAILED (failures=1)他のTestCaseとまとめて実行することもできます。
# b.py
import unittest
import random
class BarTest(unittest.TestCase):
def setUp(self):
self.seq = range(random.randint(1, 1000))
def test_len(self):
'''len(self.seq) < 100?'''
self.assert_(len(self.seq) < 100, len(self.seq))# test.py
import unittest
import b
class FooTest(unittest.TestCase):
def setUp(self):
self.seq = [i for i in range(10) if i%2]
def test_num(self):
'''reduce(lambda x, y: x*y, self.seq) < 500?'''
a = reduce(lambda x, y: x*y, self.seq)
self.assert_(a < 500, a)
def func_test():
'''func_test'''
a = 0
for i in range(10):
a += i
assert a < 30, a
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(FooTest))
suite.addTest(unittest.makeSuite(b.BarTest))
suite.addTest(unittest.FunctionTestCase(func_test))
unittest.TextTestRunner(verbosity=2).run(suite)実行結果は以下のようになります。
reduce(lambda x, y: x*y, self.seq) < 500? ... FAIL
len(self.seq) < 100? ... ok
func_test ... FAIL
======================================================================
FAIL: reduce(lambda x, y: x*y, self.seq) < 500?
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 12, in test_num
self.assert_(a < 500, a)
AssertionError: 945
======================================================================
FAIL: func_test
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 19, in func_test
assert a < 30, a
AssertionError: 36
----------------------------------------------------------------------
Ran 3 tests in 0.008s
FAILED (failures=2)