E D R , A S I H C RSS

Py Unit

1. Python Unit Testing Framework PyUnit ์— ๋Œ€ํ•ด์„œ

  • ์›๋ฌธ์€ PyUnit์— ์žˆ๋Š” ๋„ํ๋จผํŠธ ๋ฌธ์„œ์ž„์„ ๋ฐํ˜€๋‘ . ๊ณต๋ถ€๊ฒธ 1์ฐจ ์ •๋ฆฌ์ค‘. ์ผ๋‹จ ์•ฝ๊ฐ„ ๋ฒˆ์—ญ์ž‘์—…๋’ค ์ •๋ฆฌํ•จ.
  • PyUnit๋Š” Python ์— ๊ธฐ๋ณธ์ ์œผ๋กœ ํฌํ•จ๋˜์–ด์žˆ๋Š” UnitTest Framework library์ด๋‹ค. ํ•˜์ง€๋งŒ, UnitTest์ž‘์„ฑ์— ๋Œ€ํ•œ ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…๋“ค์„ ์‰ฝ๊ฒŒ ๋‹ด๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์—ฌ ๊ณต๋ถ€์ค‘. (์†”์งํžˆ C++๋กœ UnitTestํ•  ์—„๋‘ ์•ˆ๋‚˜์„œ. --; Python์œผ๋กœ ๋จผ์ € ํ”„๋กœํ† ํƒ€์ž…์ด ๋˜๋Š” ๋ถ€๋ถ„์„ ์ž‘์„ฑํ•˜๊ณ  ๋‹ค๋ฅธ ์–ธ์–ด๋กœ ํฌํŒ…ํ•˜๋Š” ์‹์œผ๋กœ ํ• ๊นŒ ์ƒ๊ฐ์ค‘)

2. ๊ด€๋ จ ์‚ฌ์ดํŠธ

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๊ฐ€ ํ…Œ์ŠคํŒ…์„ ํ• ๋•Œ ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•จ์œผ๋กœ์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

~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.2. failif

~cpp 
def runTest(self):
	self.failIf(self.widget.size() <> (100,100))

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))

11.5. exception

~cpp 
def runTest(self):
	try:
		self.widget.resize(-1,-1)
	except ValueError:
		pass
	else:
		fail("expected a ValueError")


Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:24:08
Processing time 0.0160 sec