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' 클래스를 사용한다. 가장 일반적인 TestRunnerTextTestRunner이다.
~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.0331 sec