python 단위 테스트의 고정 범위


python 테스트 도구
활용단어참조에는 널리 알려져 사용되는 여러 도구들이 이미 많습니다. 특히 피트스트는 좀 더 직관적인 단언구문과 테스트결과를 제공하며, 많은 플러그 인을 보유하고 있습니다. 하지만 여러 도구들을 알아보기 전에 python 테스트에 기반이 되는 단원 테스트프레임워크의 개념과 사용법에 대해 알아보겠습니다.
오늘은 그 중 고정 장치의 종류와 사용범위에 대해 알아보겠습니다.

테스트 클러치
테스트 픽스쳐는 테스트를 수행 시 필요한 리소스 준비과 이를 정리하는 동작에 해당합니다. 단원 테스트프레임워크에서는 고정 장치를 여러 범위에서 사용할 수 있도록 기능을 제공합니다. 아래에서 테스트, 클래스, 모듈 범위를 위한 고정 장치사용법에 대해 알아보겠습니다.

테스트 범위
테스트 용례의 각 테스트에서 사용될 고정 장치의 준비와 정리는 설정()과 찢어지다을 사용하게 됩니다. 테스트마다 반복될 수 있는 고정 장치를 설정()메소드와 찢어지다메소드를 사용해 분리할 수 있습니다. 테스트 프레임워크가 각 테스트 실행 전후에 두 메소드를 자동으로 호출합니다.
import unittest


class RemainderTest(unittest.TestCase):
  def setUp(self) -> None:
    self.number = 2
    print('setUp')

  def tearDown(self) -> None:
    print('tearDown')

  def test_even(self):
    self.assertEqual(self.number % 2, 0)

  def test_odd(self):
    self.assertNotEqual(self.number % 2, 1)
위 테스트를 실행한 결과를 통해 각 테스트별로 매번 고정 장치가 생성되는 것을 알 수 있습니다.
setUp
tearDown
setUp
tearDown
설정()메소드 작업에서 예외가 발생하면 찢어지다메소드는 호출되지 않습니다.
class RemainderTest(unittest.TestCase):
    def setUp(self) -> None:
        self.number = 2
        print('setUp')
        raise Exception()

    def tearDown(self) -> None:
        print('tearDown')

    def test_even(self):
        self.assertEqual(self.number % 2, 0)

    def test_odd(self):
        self.assertNotEqual(self.number % 2, 1)
setUp
Error
Traceback (most recent call last):
...
setUp
Error
Traceback (most recent call last):
...
설정메소드 실행이 성공했다면 테스트 실패 여부과 관계없이 찢어지다메소드가 호출됩니다.
테스트 중에 사용된 자원을 정리하기 위해 찢어지다이후에 불리는 함수를 추가할 수 있습니다. 추가된 순서의 반대 순서(후진선출)로 불리게 됩니다. 함수가 추가될 때 addCleanup에 같이 전달된 위치 인자나 키워드 인자와 함께 호출됩니다.
def cleanUp():
    print('cleanUp')


class RemainderTest(unittest.TestCase):
    def setUp(self) -> None:
        self.number = 2
        print('setUp')
        self.addCleanup(cleanUp)

    def tearDown(self) -> None:
        print('tearDown')

    def test_even(self):
        self.assertEqual(self.number % 2, 0)
setUp
tearDown
cleanUp
또한 설정메소드에서 예외가 발생하면 찢어지다메소드가 호출되지 않아도 doCleanups메소드가 실행됩니다.
def cleanUp():
    print('cleanUp')


class RemainderTest(unittest.TestCase):
    def setUp(self) -> None:
        self.number = 2
        print('setUp')
        self.addCleanup(cleanUp)
        raise Exception()

    def tearDown(self) -> None:
        print('tearDown')

    def test_even(self):
        self.assertEqual(self.number % 2, 0)
setUp
cleanUp

Error
Traceback (most recent call last):
doCleanUp메소드를 직접 호출하여 addCleanUp에 추가된 함수들을 찢어지다호출 이전에 정리할 수도 있습니다.
def cleanUp():
    print('cleanUp')


class RemainderTest(unittest.TestCase):
    def setUp(self) -> None:
        self.number = 2
        print('setUp')
        self.addCleanup(cleanUp)

    def tearDown(self) -> None:
        print('tearDown')

    def test_even(self):
        self.assertEqual(self.number % 2, 0)
        self.doCleanups()
setUp
cleanUp
tearDown

범주 범위
시험반범위에 고정 장치는 단원 테스트.테스트 키트에 의해 관리되는 클래스 설정와 최루 수업가 있습니다. 해당 고정 장치는 시험반의 모든 테스트와 공유되는 점을 주의해야 합니다. 아래와 같이 특정 테스트에서 str 목록의 수정이 일어나게 되면 이후 테스트에 영향을 미치게 됩니다.
class JoinTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.str_list = ['foo', 'bar']
        print('setUpClass')

    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')

    def test_join_with_colon(self):
        expected = 'foo:bar'
        self.assertEqual(':'.join(self.str_list), expected)
        self.str_list.append('baz')

    def test_join_with_comma(self):
        expected = 'foo,bar'
        self.assertEqual(','.join(self.str_list), expected)
        self.str_list.append('baz')
클래스 설정메소드 작업에서 예외가 발생하면 최루 수업메소드는 호출되지 않습니다.
테스트 범위와 마찬가지로 깨끗이 정리하다을 위한 addClassCleanup메소드를 제공합니다. 클래스 설정메소드에서 예외가 발생하면 최루 수업메소드가 호출되지 않아도 doClassCleanups메소드가 실행됩니다.
def classCleanUp():
    print('classCleanUp')


class JoinTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.str_list = ['foo', 'bar']
        print('setUpClass')
        cls.addClassCleanup(classCleanUp)

    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')

    def test_join_with_colon(self):
        expected = 'foo:bar'
        self.assertEqual(':'.join(self.str_list), expected)
addClassCleanup에 추가된 함수들을 최루 수업이전에 정리하고 싶다면 doClassCleanups메소드를 사용해 미리 정리할 수 있습니다.

모듈 범위
모듈 범위에서는 설정 모듈과 해체 모듈을 제공하며 함수로 작성되어야 합니다. 마찬가지로 설정 모듈실행 중 예외가 발생하면 해체 모듈함수는 호출되지 않습니다.
def setUpModule():
    print('setUpModule')


def tearDownModule():
    print('tearDownModule')


class JoinTest(unittest.TestCase):
    def test_join_with_colon(self):
        expected = 'foo:bar'
        self.assertEqual(':'.join(['foo', 'bar']), expected)


class RemainderTest(unittest.TestCase):
    def test_even(self):
        self.assertEqual(2 % 2, 0)
setUpModule
tearDownModule
모듈 범위에 깨끗이 정리하다을 위해 단원 테스트.addModuleCleanup함수를 제공합니다. 해체 모듈함수가 호출된 이후 단원 테스트.사건도모둘리 처치함수를 통해 추가된 깨끗이 정리하다함수들이 호출됩니다.
def setUpModule():
    print('setUpModule')


def tearDownModule():
    print('tearDownModule')


def moduleCleanUp():
    print('moduleCleanUp')


class JoinTest(unittest.TestCase):
    def test_join_with_colon(self):
        expected = 'foo:bar'
        self.assertEqual(':'.join(['foo', 'bar']), expected)
        unittest.addModuleCleanup(moduleCleanUp)
setUpModule
tearDownModule
moduleCleanUp
해체 모듈이 호출되기 전에 추가된 정리 함수들을 호출하고 싶다면 단원 테스트.사건도모둘리 처치함수를 직접 호출합니다.
def setUpModule():
    print('setUpModule')


def tearDownModule():
    print('tearDownModule')


def moduleCleanUp():
    print('moduleCleanUp')


unittest.addModuleCleanup(moduleCleanUp)


class JoinTest(unittest.TestCase):
    def test_join_with_colon(self):
        expected = 'foo:bar'
        self.assertEqual(':'.join(['foo', 'bar']), expected)
        unittest.case.doModuleCleanups()


class RemainderTest(unittest.TestCase):
    def test_even(self):
        self.assertEqual(2 % 2, 0)
setUpModule
moduleCleanUp
tearDownModule

전체적인 고정 범위흐름
def setUpModule():
    print('setUpModule')


def tearDownModule():
    print('tearDownModule')


def cleanUp():
    print('cleanUp')


def classCleanUp():
    print('classCleanUp')


def moduleCleanUp():
    print('moduleCleanUp')


unittest.addModuleCleanup(moduleCleanUp)


class JoinTest(unittest.TestCase):
    def setUp(self) -> None:
        print('setUp')
        self.addCleanup(cleanUp)

    def tearDown(self) -> None:
        print('tearDown')

    @classmethod
    def setUpClass(cls) -> None:
        print('setUpClass')
        cls.addClassCleanup(classCleanUp)

    @classmethod
    def tearDownClass(cls) -> None:
        print('tearDownClass')

    def test_join_with_colon(self):
        expected = 'foo:bar'
        self.assertEqual(':'.join(['foo', 'bar']), expected)
위에서 언급된 고정 범위를 모두 포함한 결과를 보면 호출 순서는 아래와 같습니다.
setUpModule
setUpClass
setUp
tearDown
cleanUp
tearDownClass
classCleanUp
tearDownModule
moduleCleanUp

정리
python 단위 테스트를 사용해 테스트 작성 시 고정 장치별 범위와 호출 시점과 조건을 잘 파악해야 합니다. 여러 범위의 고정 장치를 잘 활용하면 중복을 해결하고 테스트 성능에 도움이 되지만 잘못 사용할 경우 의도와 다른 테스트 결과가 출력될 수 있음에 주의하며 사용하는 것이 좋습니다.

참고
  • https://docs.python.org/ko/3/library/unittest.html
  • 좋은 웹페이지 즐겨찾기