Python ã®æ°ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ (or unittest2)
ãã㯠Python3 Advent Calendar ã®è¨äºã§ã。夢ã¯ãã¹ãã¨ã³ã¸ãã¢ã§ã!ã¨ãããã¨ã§ã¦ããããã¹ãã«ã¤ãã¦æ¸ãã¾ã。
Python3 ç¸ãã¨ã®ãã¨ã§ãã、ãã®æ°ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ã¯ Python 3.2 以é㨠2.7 以éã対象ã§ã。ãã以åã®ãã¼ã¸ã§ã³ã§ãã®æ°ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ãå©ç¨ãããå ´åã¯、ãããã unittest2py3k (3 ç³»)、 unittest2 (2 ç³») ã¨ããããã¯ãã¼ããç¨æããã¦ãã¾ã。æ°ã¦ããããã¹ã㯠mock ã IronPython çã®éçºè ã¨ãã¦ãç¥ããã¦ãã Michael Foord æ°ãä¸å¿ã«éçºããã¾ãã。
>>> Python ã¨ã¦ããããã¹ãã®æ´å²
Python ã®ã¦ããããã¹ãã¯、1999 å¹´ xUnit ãã¡ããªã¼ã® PyUnit ã¨ãã¦éçºãã、2001 å¹´ã«å ¬éããã Python 2.1 ãã unittest ã¨ãã¦æ¨æºã©ã¤ãã©ãªã¨ãªãã¾ãã。ãã以é、ã¢ããã°ã¬ã¼ãã¨ããã° assert* ã¡ã½ããã®è¿½å ãåé¤ã¨ãã£ãæã。PyCon 2010 ã§ã® Michael Foord æ°ã®ãã¬ã¼ã³ãã¼ã·ã§ã³ã«ãã㨠"Python ã«ã¯é©æ°çãªãã¹ãã¤ã³ãã©ãæ°å¤ãããã¾ãã、unittest ã¯æ¨æºã©ã¤ãã©ãªã¨ããçç±ã«ããæãå©ç¨ããã¦ãããã¹ããã¬ã¼ã ã¯ã¼ã¯ã§ã。ããã、ä»ã®ãã¹ããã¬ã¼ã ã¯ã¼ã¯ãé©æ°çãªé²æ©ãéãã¦ããä¸、unittest ã¯é ããåã£ã¦ãã¾ã"。
ãããã¤ãã«、ã¦ããããã¹ã㯠Python 3.2, 2.7 ã§é©æ°ããããã¨ã«ãªãã¾ãã。ããã Python ããã "å¾æ¹äºæ" ãããªãæèããã¦ãã¾ã。ããã Michael Forrd æ°ã®è¨èãåãã㨠"ããã¯é©å½ã§ã¯ãªã、é²åã§ã"。
>>> ã©ãã "é²å" ããã®ã
æ°ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ã«ã¯ä»¥ä¸ã®æ©è½ã®è¿½å ãæ´æ°ãè¡ããã¦ãã¾ã。
- 便å©ãª
assert*
ã¡ã½ããã®è¿½å - å称ã®çµ±ä¸、éè¤ã®æé¤
- ã³ãã³ãã©ã¤ã³ããã®å¶å¾¡ããã便å©ã« / æãã¯ãã£ã¹ã«ããª
- ãã¹ãã®ã¹ããã
- ã¢ã¸ã¥ã¼ã«ã¬ãã«、ã¯ã©ã¹ã¬ãã«ã®ãã¹ããã£ã¯ã¹ãã£
ãªã©ãªã©ã§ã。åé²åã®è©³ç´°ã«ã¤ãã¦è§£èª¬ãã¦ããããã¨æãã¾ã。
>>> 便å©ãª assert*
ã¡ã½ããã®è¿½å
追å ãããã¡ã½ããã«ã¯ä»¥ä¸ãå«ã¾ãã¦ãã¾ã。
- 3.1 以éã«è¿½å ãããã¡ã½ãããå«ãã§ãã¾ã
- 3.2 / 3.3 ã§éæ¨å¥¨ / å»æ¢ãããã¡ã½ããã¯å«ãã§ãã¾ãã
- 3.2 / 3.3 ã§å称å¤æ´ããããã®ã¯æ°å称ã§è¨è¼ãã¦ãã¾ã
- å©ç¨ã§ããã¡ã½ããã¯ãã¼ã¸ã§ã³ã«ãã£ã¦éãã®ã§、確èªããå¿ è¦ãããã¾ã
ã¡ã½ãã | æ¤è¨¼å 容 | Ver. |
---|---|---|
assertIsNone / assertIsNotNone(x, msg=None) | x is [not] None | >= 3.1 |
assertIs / assertIsNot(a, b) | a is [not] b | >= 3.1 |
assertIn / assertNotIn(a, b) | a [not] in b | >= 3.1 |
assertIsInstance / assertNotIsInstance(a, b) | [not] isinstance(a, b) | >= 3.2 |
assertGreater / assertGreaterEqual(a, b) assertLess / assertLessEqual(a, b) | a >[=] b a <[=] b | >= 3.1 |
assertAlmostEqual / assertNotAlmostEqual(a, b) | round(a-b, 7) [!|=]= 0 ※ ä»æ§å¤æ´ (delta ã追å ) | >= 3.2 |
assertRegex / assertNotRegex(s, re) | [not] re.search(s) | >= 3.1 (not 㯠>= 3.2) |
assertCountEqual(a, b) | é åã®åæ°ã¨å¤ | >= 3.2 |
assertMultiLineEqual(a, b) | a = b: æåå | >= 3.1 |
assertSequenceEqual(a, b) | a = b: é å+ã¿ã¤ã | >= 3.1 |
assertListEqual(a, b) | a = b: list | >= 3.1 |
assertTupleEqual(a, b) | a = b: tuple | >= 3.1 |
assertSetEqual(a, b) | a = b: set | >= 3.1 |
assertDictEqual(a, b) | a = b: dict | >= 3.1 |
assertRaises(exc, fun, *args, **kwds) | fun(*args, **kwds) raises exc | >= 3.1 ※ 3.2, 3.3 ã§ä»æ§å¤æ´ |
assertRaisesRegex(exc, re, fun, *args, **kwds) | fun(*args, **kwds) raises exc \ and re.match(exc.message) | >= 3.1 ※ 3.2, 3.3 ã§ä»æ§å¤æ´ |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds) raises warn | >= 3.2 |
assertWarnsRegex(warn, re, fun, *args, **kwds) | fun(*args, **kwds) raises warn \ and re.match(exc.message) | >= 3.2 |
ãã㯠Google ããã®ååãå¾ã¦è¿½å ãããããã§ã。ãã®ã¡ãªãããããã¤ãæãããã¨æãã¾ã。
ã¾ã、a=b çã®ç¶æ
æ¤è¨¼ç¨ã®ã¡ã½ãããå¢ãããã¨。ãã¹ãéçºè
ã®çæ§ãå¾
ã¡æãã§ããã®ã§ã¯ãªãã§ãããã?ãªãã§ããã幸ããªã®ã。æ¢åã® assert*
ãé§ä½¿ãã¦ä½ã£ã¦ãã¡ã½ããã 1 è¡ã§æ¤è¨¼ã§ããããã«ãªã、ãã°ãçºè¦ãããããªãã¾ã。
class TestTest(unittest.TestCase): def setUp(self): self.list_first = [1, 2, 3, 4, ] self.list_second = [1, 2, 4, 4, 5, ] def test_old(self): self.assertEqual(len(self.list_first), len(self.list_second)) self.assertEqual(self.list_first, self.list_second) def test_new(self): self.assertCountEqual(self.list_first, self.list_second) if __name__ == '__main__': unittest.main()
ä¾ããã¡ããã¡ãã§ããããªãã。。。
====================================================================== FAIL: test_new (__main__.TestTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_test.py", line 14, in test_new self.assertCountEqual(self.list_first, self.list_second) AssertionError: Element counts were not equal: First has 1, Second has 0: 3 First has 1, Second has 2: 4 First has 0, Second has 1: 5 ====================================================================== FAIL: test_old (__main__.TestTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_test.py", line 10, in test_old self.assertEqual(len(self.list_first), len(self.list_second)) AssertionError: 4 != 5 ---------------------------------------------------------------------- Ran 2 tests in 0.000s
ãã¹ãçµæãè¦ã¦ã、4 != 5
ãããªãã®ãã¨ããã£ã±ãã§ããã。以å㯠"ãã¹ãã¡ã½ããæ° = æ©è½æ°" ã¨ããã®ãä¸è¬çã§ããã、ã¢ãã³ãªãã¹ã㯠"ãã¹ãã¡ã½ãã ≒ æ¤è¨¼" ã§ã。ã²ã¨ã¤ã®ãã¹ãã¡ã½ãã (test_*
) å
ã«è¤æ°ã®æ¤è¨¼ (assert*
) ãæ¸ããå ´å、è¤æ°ã®ãã¹ãã«å¤±æãã¦ãæåã®å¤±æããåºåããã¾ãã。「åå²ããã¨æéããããã、ã¾ã¨ããã¨å¤±æããã¨ãã«è¿½ããããã®å¤§å¤ã ã。。。ããã¼ãªãç¬èªã® assert*
å®ç¾©ããã」ã£ã¦ãªæ©ã¿ãã解æ¾ããã¾ã。
ã¾ã、失ææã®çµæåºåããã°ãåãããããããã«å¤§å¹ ã«æ¹åããã¦ãã¾ã。
### Python 2.6 AssertionError: [1, 2, 3, 4] != [1, 2, 4, 4, 5]
### Python 3.2 AssertionError: Lists differ: [1, 2, 3, 4] != [1, 2, 4, 4, 5] First differing element 2: 3 4 Second list contains 1 additional elements. First extra element 4: 5 - [1, 2, 3, 4] ? ^ + [1, 2, 4, 4, 5] ? ^ +++
é©æé©æã® assert*
ã使ããã¨ã§、ãã°ãè¦ã¤ãããããªãã¾ã。
次ã«、assertRaises ã大ããªå¤æ´ã® 1 ã¤ã§ã。以å㯠try-catch
ã§æ¸ãã¦ããæ¤è¨¼ã 1 è¡ã§æ¸ããããã«ãªãã¾ãã。
class TestTest(unittest.TestCase): def test_old(self): try: int("spam") self.fail("Expected a ValueError") except (Exception, ) as e: self.assertTrue(isinstance(e, ValueError)) def test_new(self): self.assertRaises(ValueError, int, "spam") if __name__ == '__main__': unittest.main()
ãã®ãã¹ãã¯ãã 1 ã¤åé¡ãããã¾ã。ä¾å¤ãçºçããªãå ´åã¯、"self.fail
" ãéããªããã¨ã§ãã。ãã¹ãã®ã«ãã¬ãã¸ãéè¦ã§ãã、ãã¹ãèªä½ã®ã«ãã¬ãã¸ã大åã§ã。try
ç¯ãé·ããªã£ãã、ãã¹ããããããã¨ãã¹ããè¤éã«ãªã£ã¦ãã¾ãã¾ã。éããªãå¯è½æ§ãããã³ã¼ããªãã¦æ¸ããªãããã«ãã¾ããã (æ¸ãã¦ã¦è³ãçã)。
>>> å称ã®çµ±ä¸、éè¤ã®æé¤
追å ããã便å©æ©è½ãããã°、éæ¨å¥¨ã«ãªã£ããã®ãããã¾ã。å称ã®çµ±ä¸ã¨éè¤ã®è§£é¤ã«ãã、以ä¸ã®ã¡ã½ãããéæ¨å¥¨ã«ãªãã¾ãã。
assert_
:assertTrue
ãå©ç¨ããfail*
:assert*
ãå©ç¨ããassertEquals:
assertEqual
ã«çµ±ä¸
ãã¼ã¸ã§ã³ã«ãã£ã¦æ¨å¥¨・éæ¨å¥¨ã¯ç°ãªã£ã¦ãã¾ã。
>>> ã³ãã³ãã©ã¤ã³ããã®å¶å¾¡ããã便å©ã« / æãã¯ãã£ã¹ã«ããª
unittest
ã¢ã¸ã¥ã¼ã«ã¯ã³ãã³ãã©ã¤ã³ãã使ãã¾ã (åè)。ããã«、py.test ã nose ã¨ã¾ã§ã¯ããã¾ããã、ãã£ã¹ã«ããªæ©è½ã追å ããã¾ãã!
# ãã¹ãã¢ã¸ã¥ã¼ã«ããã¹ãã¯ã©ã¹ãæå®ãã¦å®è¡ python -m unittest test_module1 test_module2 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method # ãã¡ã¤ã«åãæå®ãã¦å®è¡ python -m unittest tests/test_something.py # çµæã®è©³ç´°åºå (verbosity=3) python -m unittest -v test_module # ãã£ã¹ã«ããªå®è¡ <- New!!! python -m unittest python -m unittest discover
ãã£ã¹ã«ãã¼ã¨ä¾¿å©ãªãªãã·ã§ã³ãã¡。
-v
,--verbose
: çµæã詳細ã«åºåãã-f
,--failfast
: æåã«å¤±æããæã§ãã¹ããçµäºãã (>= 3.2)-c
,--catch
: ãã¹ããä¸æã、ããã¾ã§ã«å®è¡ãããã¹ãçµæãåºåãã (>= 3.2)-b
,--buffer
: åºåå ãæå®ãã (>= 3.2)-s
: ãã£ã¹ã«ããªãéå§ãããã£ã¬ã¯ã㪠(>= 3.2)-p
: ãã£ã¹ã«ããªæã«ãã¹ããã¡ã¤ã«ã«ãããããããã¿ã¼ã³ (>= 3.2)-t
: ãã£ã¹ã«ããªããããããã¸ã§ã¯ãã®ãããã¬ãã«ãã£ã¬ã¯ã㪠(>= 3.2)
-v
, -f
, -c
, -b
ã«ã¤ãã¦ã¯ãã£ã¹ã«ããªæ以å¤ã§ã使ãã¾ã。
python -m unittest discover -s project_directory -p '*_test.py' python -m unittest discover project_directory '*_test.py'
ãã£ã¹ã«ããªã®ããã«、load_tests
ãããã³ã«ã追å ããã¦ãã¾ã。ãã¹ãã¢ã¸ã¥ã¼ã«ã« TestSuite
ãè¿ã load_tests
ãå®ç¾©ãããã¨ã«ãã、ãã¹ããå¶å¾¡ãããã¨ãã§ãã¾ã。ä¾ãã°ä»¥ä¸ã®ä¾ã§ã¯ãã£ã¹ã«ããªããéã®ãã¹ãã¯ã©ã¹ãéå®ãã¦ãã¾ã。
test_cases = (TestCase1, TestCase2, TestCase3) def load_tests(loader, tests, pattern): suite = TestSuite() for test_class in test_cases: tests = loader.loadTestsFromTestCase(test_class) suite.addTests(tests) return suite
éå®ããªããã°、TestCase
ãç¶æ¿ãããã¹ã¦ã®ã¯ã©ã¹ããã¹ã対象ã¨ãªãã¾ã。
>>> ãã¹ãã®ã¹ããã
ãã¹ãã¯ã©ã¹ã¨ãã¹ãã¡ã½ãããã¹ãããåºæ¥ãããã«ãªãã¾ãã。ã¹ãããããæ¡ä»¶ãæå®ã§ãã¾ã。
class TestTest(unittest.TestCase): @unittest.skip("ç¡æ¡ä»¶ã¹ããã") def test_nothing(self): self.fail("shouldn't happen") @unittest.skipIf(mylib.__version__ < (1, 3), "not supported in this library version") def test_format(self): # æå®ãããã¼ã¸ã§ã³ã®ã©ã¤ãã©ãªã§ã®ã¿ãã¹ã pass @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_windows_support(self): # Windows ã§ã®ã¿ãã¹ã pass
verbosity ãæå®ãã¦å®è¡ããã¨ä»¥ä¸ã®ããã«åºåããã¾ã。
$ python -m unittest -v test.py test_nothing (test_test.TestTest) ... skipped 'ç¡æ¡ä»¶ã¹ããã' test_format (__main__.TestTest) ... skipped 'not supported in this library version' test_windows_support (__main__.TestTest) ... skipped 'requires Windows'
ãã¹ãã¯ã©ã¹ãã¨ã¹ãããããå ´åã¯、以ä¸ã®ããã«æ¸ãã¾ã。
@skip("showing class skipping") class TestTest(unittest.TestCase): def test_not_run(self): pass
ã©ãããå ´åã«æå¹ãªã®ããããã¾ããã 失æããå ´åã§ã失æã¨æ°ããªãããã«ãããã³ã¬ã¼ã¿ã追å ããã¦ãã¾ã。ãã¹ãä½ææã«ã¯å¤±æãããã©、ä¿®æ£ãã¦ãã¹ããæåããã¨éç¥ãã¦ããã¾ã。
@unittest.expectedFailure def test_fail(self): self.assertEqual(1, 0, "broken")
# ãã¹ãã«æåãã¦ãã¾ã£ãå ´åã®åºå test_fail (test_test.TestTest) ... unexpected success
>>> ã¢ã¸ã¥ã¼ã«ã¬ãã«、ã¯ã©ã¹ã¬ãã«ã®ãã¹ããã£ã¯ã¹ãã£
ããããã¹ãã³ã¼ãã綺éºã«æ¸ãããæ¹ã«ã¨ã£ã¦å¾
ã¡æãã§ããæ©è½ã ã¨æãã¾ã。setUp
/ tearDown
ãç¡é§ã«é·ãã£ãã、ãã¹ãã¡ã½ããéã§å
±æãããã¯ã©ã¹ã¬ãã«å¤æ°ãã¡ã½ããã¬ãã«ã«å®ç¾©ããã。。。ããã¯ç¥ã¢ãããã¼ãã§ã。ãã¹ããã·ã³ãã«ã«、ã¡ã³ãã® ã¹ã³ã¼ããæå°éã«æãããã¨ãã§ãã¾ã!!!ããããã°ã¢ãã³ãª xUnit ã§ã¯ setUp/tearDown ããªããã¡ãã£ãè¨èªãããã¾ãã。
# Old... orz connection = createExpensiveConnectionObject() class TestTest(unittest.TestCase): def test_spam(self): connection
ã³ãã¯ã·ã§ã³ãéãå ´åã¯å¥ã¢ã¸ã¥ã¼ã« orz...
# New !!! class TestTest(unittest.TestCase): @classmethod def setUpClass(cls): cls._connection = createExpensiveConnectionObject() @classmethod def tearDownClass(cls): cls._connection.destroy() def test_spam(self): cls._connection
ã¹ã³ã¼ããç´ æµã§ããã!ã¹ã³ã¼ãç¯å²ã®ããã«ã¢ã¸ã¥ã¼ã«ãã¯ã©ã¹ãåå²ããå¿
è¦ããªããªãã¾ã!ä¸è¿°ã®ããã« "ãã¹ãã¡ã½ãã ≒ æ¤è¨¼" å½¢å¼ã§æ¸ãã¦ã、ç¡é§ã« setUp
ãå®è¡ããªãã§æ¸ã¿ã¾ã。
ã¢ã¸ã¥ã¼ã«ã¬ãã«ã¯ä»¥ä¸ã®ããã«æ¸ãã¾ã。
def setUpModule(): createConnection() def tearDownModule(): closeConnection()
setUp
/ tearDown
ã®ä»£æ¿æ段ã¨ã㦠addCleanup
ã¨ããã¡ã½ãããä½ããã¦ãã¾ã。 addCleanup
ã使ããã¨ã§、å¯èªæ§ãé«ããªã½ã¼ã¹ã®ã»ããã¢ããå¦ç / çµäºå¦çãæ¸ããã¨ãã§ããããã«ãªãã¾ã。 ããã¯ãã¹ãã®çµäºæ (LIFO) ã®åä½ãä¿éããã¦ãã¾ã。tearDown
㯠setUp
失ææã«ã¯åä½ãã¾ãã。
def test_method(self): temp_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, temp_dir) ...
æ示çã«å¼ã³åºãããå ´åã¯、以ä¸ã®ããã«æ¸ãã¾ã。
def test_method(self): temp_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, temp_dir) ... self.doCleanups() ... self.doCleanups()
>>> ã¾ã¨ã
æ°ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ç´ æ´ãããã§ããã!ããã¯ãã¼ããç¨æããã¦ãã¾ãã®ã§、æ¯é移è¡ãã¾ããã!ããã¯ãã¼ãã«ã¯ãã£ã¹ã«ããªçã«å¶éãããã¾ãã、assert*
ã¡ã½ããããã¹ããã£ã¯ã¹ãã£ã®æ©æµãåãããã¨ãã§ãã¾ã。ããã¯ãã¼ãã¨ã®å¾æ¹äºæãæããããã、以ä¸ã®ããã« import
ãããã¨ãæ¨å¥¨ããã¦ãã¾ã。
try: import unittest2 as unittest except (ImportError): import unittest
æ°ã¦ããããã¹ããã¬ã¼ã ã¯ã¼ã¯ã«ã¯ä»ã«ãå¤ãã®ä¾¿å©æ©è½ãç¨æããã¦ãã¾ã。詳細ã«ã¤ãã¦ã¯å ¬å¼ããã¥ã¡ã³ããåç §ãã¦ãã ãã。
>>> Python 3 ã«å¯¾å¿ãã¦ãããã¹ãã½ãªã¥ã¼ã·ã§ã³
- ãã¹ããã¬ã¼ã ã¯ã¼ã¯: nose, py.test
- ãã¹ãç¨ã¢ãã¯: mock
- TDD ãªã㬠BDD (ããã¤ãã¢ããªãã³éçº) ãã¬ã¼ã ã¯ã¼ã¯: python-specfor
BDDãã¬ã¼ã ã¯ã¼ã¯ã® PyCcuracy çãä»å¾ã®å¯¾å¿ã«æå¾ ãããã§ãã。
ã¨ãããã¨ã§æ¬¡ã¯æè¿ãåæ§ãèªçããã "@mopemope" å çã«ããã³ã¿ããã§ã。
ã³ã¡ã³ã
ã³ã¡ã³ããæ稿