Contents
- 1. Python Unit Testing Framework PyUnit ์ ๋ํด์
- 2. ๊ด๋ จ ์ฌ์ดํธ
- 3. TestCase
- 4. ๊ฐ๋จํ testcase ์ ์ ์. 'failure', 'error'
- 5. ์ฌ์ฌ์ฉํ๋ set-up code : 'fixtures' ๋ง๋ค๊ธฐ.
- 6. ์ฌ๋ฌ๊ฐ์ test method๋ฅผ ํฌํจํ TestCase classes
- 7. TestSuite : testcase๋ค์ ์งํฉ์ฒด
- 8. test suite๋ค์ ์งํฉ. ๋ชจ๋ ํ ์คํธ๋ค์ ํ๋ฒ์~
- 9. ์ด๋์ ํ ์คํธ์ฝ๋๋ฅผ ๋๊น?
- 10. Test์ ์คํ
- 11. Test ์กฐ๊ฑด๋ค์ ๋ํด์
1. Python Unit Testing Framework PyUnit ์ ๋ํด์ ¶
- ์๋ฌธ์ PyUnit์ ์๋ ๋ํ๋จผํธ ๋ฌธ์์์ ๋ฐํ๋ . ๊ณต๋ถ๊ฒธ 1์ฐจ ์ ๋ฆฌ์ค. ์ผ๋จ ์ฝ๊ฐ ๋ฒ์ญ์์
๋ค ์ ๋ฆฌํจ.
- PyUnit๋ Python ์ ๊ธฐ๋ณธ์ ์ผ๋ก ํฌํจ๋์ด์๋ UnitTest Framework library์ด๋ค. ํ์ง๋ง, UnitTest์์ฑ์ ๋ํ ๊ธฐ๋ณธ์ ์ธ ๊ฐ๋
๋ค์ ์ฝ๊ฒ ๋ด๊ณ ์๋ค๊ณ ์๊ฐํ์ฌ ๊ณต๋ถ์ค. (์์งํ C++๋ก UnitTestํ ์๋ ์๋์. --; Python์ผ๋ก ๋จผ์ ํ๋กํ ํ์
์ด ๋๋ ๋ถ๋ถ์ ์์ฑํ๊ณ ๋ค๋ฅธ ์ธ์ด๋ก ํฌํ
ํ๋ ์์ผ๋ก ํ ๊น ์๊ฐ์ค)
2. ๊ด๋ จ ์ฌ์ดํธ ¶
- http://c2.com/cgi/wiki?PythonUnit - ์์ง ์์ฝ์ด๋ด์. --; ํฝํ๊ณ ์ ๊ธด ๋ญํ์ง๋ง. --a
- http://pyunit.sourceforge.net
- http://junit.org - Java Unit Test Framework ๋ชจ๋.
3. TestCase ¶
unit testing ์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ฝ๋ ๋ธ๋ก ๋จ์. ์
ํ
๊ณผ ๋ชจ๋์ด ์ ๋๋ก ๋์๊ฐ๋์ง๋ฅผ ์ฒดํฌํ๊ธฐ ์ํ ํ๋์ ์๋๋ฆฌ์ค๊ฐ ๋๋ค.
PyUnit์์๋ TestCase๋ unittest๋ชจ๋์ TestCase ํด๋์ค๋ก์ ํํ๋๋ค.testcase ํด๋์ค๋ฅผ ๋ง๋ค๋ ค๋ฉด unittest.TestCase๋ฅผ ์์๋ฐ์์ ๋ง๋ค๋ฉด ๋๋ค.
4. ๊ฐ๋จํ testcase ์ ์ ์. 'failure', 'error' ¶
๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ runTest ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉ ํ๋ ๊ฒ์ด๋ค.
~cpp import unittest class DefaultWidgetSizeTestCase(unittest.TestCase): def runTest(self): widget = Widget("The widget") assert widget.size() == (50,50), 'incorrect default size'ํ ์คํ ์ ํ๊ธฐ ์ํด Python์ assert ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ค. testcase๊ฐ ์คํ๋ ๋ assertion์ ์คํํ๋ฉด AssertionError ๊ฐ ์ผ์ด๋๊ณ , testing framework๋ ์ด testcase๋ฅผ 'failure' ํ๋ค๊ณ ์ ์ํ ๊ฒ์ด๋ค. 'assert' ๋ฅผ ๋ช ์์ ์ผ๋ก ์จ๋์ง ์์ ๋ถ๋ถ์์์ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒ๋ค์ testing framework ์์๋ 'errors'๋ก ๊ฐ์ฃผํ๋ค.
testcase๋ฅผ ์คํํ๋ ๋ฐฉ๋ฒ์ ํ์ ์ค๋ช
ํ ๊ฒ์ด๋ค. testcase ํด๋์ค๋ฅผ ์์ฑํ๊ธฐ ์ํด ์ฐ๋ฆฌ๋ ์์ฑ์์ ์๋ฌด ์ธ์ ์์ด ํธ์ถํด์ฃผ๋ฉด ๋๋ค.
~cpp testCase = DefaultWidgetSizeTestCase ()
5. ์ฌ์ฌ์ฉํ๋ set-up code : 'fixtures' ๋ง๋ค๊ธฐ. ¶
๋ง์ผ testcase๊ฐ ๋ง์์ง๋ฉด ๊ทธ๋ค์ set-up ์ฝ๋๋ค๋ ์ค๋ณต๋ ๊ฒ์ด๋ค. ๋งค๋ฒ Widget ํด๋์ค๋ฅผ ํ
์คํธํ๊ธฐ ์ํด ํด๋์ค๋ค๋ง๋ค widget ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๊ฒ์ ๋ช
๋ฐฑํ ์ค๋ณต์ด๋ค.
๋คํ์ค๋ฝ๊ฒ๋ ์ฐ๋ฆฌ๋ setUp ๋ผ๋, testing framework๊ฐ ํ ์คํ ์ ํ ๋ ์๋์ผ๋ก ํธ์ถํด์ฃผ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํจ์ผ๋ก์ ํด๊ฒฐํ ์ ์๋ค.
๋คํ์ค๋ฝ๊ฒ๋ ์ฐ๋ฆฌ๋ setUp ๋ผ๋, testing framework๊ฐ ํ ์คํ ์ ํ ๋ ์๋์ผ๋ก ํธ์ถํด์ฃผ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํจ์ผ๋ก์ ํด๊ฒฐํ ์ ์๋ค.
~cpp import unittest class SimpleWidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget("The widget") class DefaultWidgetSizeTestCase(SimpleWidgetTestCase): def runTest(self): assert self.widget.size() == (50,50), 'incorrect default size' class WidgetResizeTestCase(SimpleWidgetTestCase): def runTest(self): self.widget.resize(100,150) assert self.widget.size() == (100,150), \ 'wrong size after resize'
๋ง์ผ setUp ๋ฉ์๋๊ฐ ํ
์คํธ์ค ์์ธ๋ฅผ ๋ฐ์ํ ๊ฒฝ์ฐ framework๋ ์ด ํ
์คํธ์ error๋ฅผ ๊ฐ์ง๊ณ ์๋ค๊ณ ์๊ฐํ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ runTest ๋ฉ์๋๊ฐ ์คํ๋์ง ์์ ๊ฒ์ด๋ค.
๋จ์ํ, ์ฐ๋ฆฌ๋ tearDown ๋ฉ์๋๋ฅผ ์ ๊ณตํ ์ ์๋ค. runTest๊ฐ ์คํ๋๊ณ ๋ ๋ค์ ์ผ์ ํด๊ฒฐํ๋ค.
~cpp import unittest class SimpleWidgetTestCase (unittest.TestCase): def setUp (self): self.widget = Widget ("The widget") def tearDown (self): self.widget.dispose () self.widget = None๋ง์ผ setUp ๋ฉ์๋ ์คํ์ด ์ฑ๊ณต๋๋ฉด, tearDown ๋ฉ์๋๋ runTest๊ฐ ์ฑ๊ณตํ๊ฑด ์ํ๊ฑด ์๊ด์์ด ํธ์ถ๋ ๊ฒ์ด๋ค.
์ด๋ฌํ testing code๋ฅผ ์ํ ์์
ํ๊ฒฝ์ 'fixture' ๋ผ๊ณ ํ๋ค.
6. ์ฌ๋ฌ๊ฐ์ test method๋ฅผ ํฌํจํ TestCase classes ¶
์ข
์ข
, ๋ง์ ์์ test case๋ค์ด ๊ฐ์ fixture๋ฅผ ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์ด๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ, ์ฐ๋ฆฌ๋ DefaultWidgetSizeTestCase ๊ฐ์ ๋ง์ ์์ one-method class ์์ SimpleWidgetTestCase๋ฅผ ์๋ธํด๋์ฑํ๊ฒ ๋๋ค. ์ด๊ฑด ์๊ฐ๋ญ๋น์ด๊ณ ,.. --a PyUnit๋ ๋ ๋จ์ํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ๋ค.
~cpp import unittest class WidgetTestCase (unittest.TestCase): def setUp (self): self.widget = Widget ("The widget") def tearDown (self): self.widget.dispose () self.widget = None def testDefaultSize (self): assert self.widget.size() == (50,50), 'incorrect default size' def testResize (self): self.widget.resize (100,150) assert self.wdiget.size() == (100,150), 'wrong size after resize'์ฌ๊ธฐ์๋ runTest ๋ฉ์๋๊ฐ ์๋๋์ , ๋๊ฐ์ ๋ค๋ฅธ test ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ํด๋์ค ์ธ์คํด์ค๋ ์ด์ ๊ฐ๊ฐ self.widget ์ ์ํํ๊ณ ๊ฐ ์ธ์คํด์ค์ ๋ํด ๋ฐ๋ก ์๋ฉธ๋๋ฉด์ ๊ฐ๊ฐ์ test method๋ฅผ ์คํํ๋ค.
์ธ์คํด์ค๋ฅผ ์์ฑํ ๋ ์ฐ๋ฆฌ๋ ๊ทธ ํ
์คํธ ์ธ์คํด์ค๊ฐ ์ํํ ํ
์คํธ ๋ฉ์๋๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ๋ช
์ํด์ฃผ์ด์ผ ํ๋ค. ์ด ์ผ์ constructor์ ๋ฉ์๋ ์ด๋ฆ์ ์ ์ด์ฃผ๋ฉด ๋๋ค.
~cpp defaultSizeTestCase = WidgetTestCase ("testDefaultSize") resizeTestCase = WidgetTestCase ("testResize")
7. TestSuite : testcase๋ค์ ์งํฉ์ฒด ¶
Test case ์ธ์คํด์ค๋ค์ ๊ทธ๋ค์ด ํ
์คํธํ๋ ค๋ ๊ฒ๋ค์ ๋ฐ๋ผ ํจ๊ป ๊ทธ๋ฃนํ๋๋ค. PyUnit๋ ์ด๋ฅผ ์ํ 'Test Suite' ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ๋ค. Test Suite๋ unittest ๋ชจ๋์ TestSuite class๋ก ํํ๋๋ค.
~cpp widgetTestSuite = unittest.TestSuite () widgetTestSuite.addTest (WidgetTestCase ("testDefaultSize")) widgetTestSuite.addTest (WidgetTestCase ("testResize"))
๊ฐ๊ฐ์ ํ
์คํธ ์ํ์ ์ํด (์ฐ๋ฆฌ๋ ๋์ค์ ๋ค์ ๋ณด๊ฒ ์ง๋ง), ๊ฐ๊ฐ์ ํ
์คํธ ๋ชจ๋์ 'ํธ์ถํ ์ ์๋' test suite ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด ์ข๋ค.
~cpp def suite (): suite = unittest.TestSuite () suite.addTest (WidgetTestCase ("testDefaultSize")) suite.addTest (WidgetTestCase ("testResize")) return suite๋๋
~cpp class WidgetTestSuite (unittest.TestSuite): def __init__(self): unittest.TestSuite.__init__(self, map(WdigetTestCase, "testDefaultSize", "testResize")))unittest ๋ชจ๋์๋ makeSuite ๋ผ๋ ํธ๋ฆฌํ ํจ์๊ฐ ์๋ค. ์ด ํจ์๋ test case class ์์ ๋ชจ๋ test case๋ฅผ ํฌํจํ๋ test suite๋ฅผ ๋ง๋ค์ด์ค๋ค. (์์ฐ!!)
~cpp suite = unittest.makeSuite (WidgetTestCase, 'test')makeSuite ํจ์๋ฅผ ์ฌ์ฉํ ๋ testcase๋ค์ cmp ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ํธํ ์์๋๋ก ์คํ๋๋ค.
8. test suite๋ค์ ์งํฉ. ๋ชจ๋ ํ ์คํธ๋ค์ ํ๋ฒ์~ ¶
์ข
์ข
testcase๋ค์ ํจ๊ป ๋ฌถ์ suites๋ค์ ๊ทธ๋ฃน์ ์ํ ๋๊ฐ ์๋ค. ๊ทธ๋ ๊ฒ ํจ์ผ๋ก์ ํ๋ฒ์ ๋ชจ๋ ์์คํ
์ test๋ฅผ ์ํํ ์ ์๋ค. TestSuite๋ค์ TestSuite ์ ํฌํจ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ๊ฐ๋จํ๋ค.
~cpp suite1 = module1.TheTestSuite () suite2 = module2.TheTestSuite () alltests = unittest.TestSuite ((suite1, suite2))
9. ์ด๋์ ํ ์คํธ์ฝ๋๋ฅผ ๋๊น? ¶
testcode๋ 'widgettests.py' ์ฒ๋ผ ๋ฐ๋ก ํ
์คํธ์ฝ๋๋ค์ ๋ํ ๋ชจ๋์ ๋๋ ๊ฒ์ด ์ฌ๋ฌ๊ฐ์ง๋ฉด์์ ์ฅ์ ์ ์ง๋๋ค.
- command line์์ test module๋ฅผ ๋จ๋
์ ์ผ๋ก ์คํํ ์ ์๋ค.
- ์ฝ๋์ testcode๊ฐ ์ฝ๊ฒ ๋ถ๋ฆฌ๋๋ค.
- ํน๋ณํ ์ด์ ์์ด testcode๋ฅผ test๋ฐ์ code์ ๋ง์ถ๋ ค๋ ์ ํน์ ๋ ์ ์๋ค.
- ํ
์คํธ ๋ ์ฝ๋๋ฅผ refactoring ํ๊ธฐ ๋ ์ฉ์ดํด์ง๋ค.
- ํ
์คํ
์ ๋ต์ด ๋ฐ๋์ด๋, source code๋ฅผ ๊ณ ์น ํ์๊ฐ ์์ด์ง๋ค.
10. Test์ ์คํ ¶
PyUnit test framework๋ ํ
์คํธ๋ฅผ ์ํํ๊ธฐ ์ํด 'TestRunner' ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ค. ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ TestRunner๋ TextTestRunner์ด๋ค.
~cpp runner = unittest.TextTestRunner () runner.run (widget.TestSuite)๊ธฐ๋ณธ์ ์ผ๋ก TextTestRunner๋ sys.stderr์ ์ถ๋ ฅํ๋ค. TextTestrunner ๊ฐ์ ํด๋์ค๋ Python interpreter session๊ณผ ์ํธ์์ฉํ๋ฉด์ test๋ค์ ์คํ์์ผ๋ณผ ์ ์๋ ์ด์์ ์ธ ๋ฐฉ๋ฒ์ด๋ค.
11. Test ์กฐ๊ฑด๋ค์ ๋ํด์ ¶
test ์ฝ๋๋ ๊ฐ๊ฐ์ test์กฐ๊ฑด์ ๋ง์ถฐ ๋ฌธ์ ๋ฐ์์ fail ๋ฑ์ ๋ฐ์์ํจ๋ค.
11.1. assert ¶
~cpp def runTest(self): self.assert_(self.widget.size() == (100,100), "size is wrong")
11.3. fail, failUnless ¶
~cpp def runTest(self): ... if not hasattr(something, "blah"): self.fail("blah missing") # or just 'self.fail()'
11.4. assertEqual ¶
~cpp def testSomething(self): self.widget.resize(100,100) self.assertEqual(self.widget.size, (100,100))