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.0296 sec