8퍼센트 Test case 작성 가이드

8퍼센트 / Ho Sung Lee / 17. 07. 14. 오후 2:30


8퍼센트에서 Python Django 코드에 대한 Test case 작성시 사용하는 가이드를 공유해보려고 합니다.

클래스명

  • 일반적으로 TestCase 를 상속 받는 클래스일 경우 class 명의 마지막에 TestCase 를 붙입니다.
  • 예제: SimpleTestCase(TestCase)

함수명

테스트 함수명의 경우 test_ 로만 시작하면 동작하는데 문제가 없고 테스트 코드에까지 주석을 다는 것은 번거로우므로 함수명의 test_ 뒷부분을 한글로 하여 설명을 대신하도록 합니다.

class IUPaginationMethodTestCase(TestCase):
 @classmethod
 def setUpTestData(cls):
 cls.request = Mock()
 cls.request.GET = {'page': 1, 'items_per_page': 1}
 cls.pagination = IUPagination(cls.request)
 
 def test_page_url_기본(self):
 expected = '?{}=1'.format(self.pagination.page_key)
 self.assertEqual(self.pagination.page_url(), expected)
 
 def test_page_url_쿼리스트링_없는경우_물음표_붙인다(self):
 expected = '/?{}=1'.format(self.pagination.page_key)
 self.pagination.url_prefix = '/'
 self.assertEqual(self.pagination.page_url(), expected)
 
 def test_page_url_쿼리스트링_있는경우_엠퍼센드로_붙인다(self):
 expected = '{}&{}=1'.format(
 self.pagination.url_prefix, self.pagination.page_key
 ))

 self.pagination.url_prefix = '?utm=source'
 self.assertEqual(self.pagination.page_url(), expected)

factory_boy

fixture 를 대신해서 가급적 factory_boy 를 사용합니다.

signals 끄기

  • factory boy로 모델 객체 생성시 signal 이 호출되는데 signal에 대한 테스트가 아니라면 대부분 실행할 필요가 없습니다.
  • 이 때 factory.django.mute_signals를 사용해서 끄면 됩니다.
  • decorator, context manager 둘 다 사용 가능합니다.
    • decorator
      @mute_signals(signals.post_save)
      def test_some_code(self):
       some = SomeFactory()
      
    • context manager
      with mute_signals(signals.post_save):
       some = SomeFactory()
      

참고 링크

setUpTestData vs setUp

  • fixture를 사용하면 fixture로 정의한 모델 객체가 모든 테스트 시작 전에 생성이 되는데 유사하게 setUp 에서 factory 생성을 하게 되면 매번 객체 생성을 하게 되므로 느립니다.
  • 테스트에서 read only 로만 사용하는 객체의 경우 class method인 setUpTestData 에서 생성하면 1번만 생성이 되므로 빨라집니다.
  • 가급적 setUp 에서 매번 객체를 생성하는 것을 지양하고 테스트 함수 내에서 필요한 객체만 생성하는 것이 효율적이고 빠릅니다.

method mock

메소드를 mock 하는 경우 unittest.mock.patch() 를 사용합니다.

decorator

보통 테스트 메소드에 대한 decorator 로 사용합니다.

직접 호출

  • class 내의 여러 테스트 메소드 혹은 모든 테스트 메소드에서 동일한 함수를 mock 하는 경우에는 start, stop 을 활용하면 편합니다.
  • 예제 코드
    from unittest import mock
    
    class MyTest(TestCase):
     def setUp(self):
     self.mock_method1 = mock.patch('package.module.method1').start()
     self.mock_method1 = mock.patch('package.module.method2').start()
     
     def tearDown(self):
     mock.patch.stopall()
    
     def test_something(self):
     something()
     self.assertTrue(self.mock_method1.called)
    
  • 참고 링크: patch methods start and stop

timezone

  • datetime.datetime.now() datetime.datetime.strptime() 등을 사용해서 naive datetime 객체를 django 모델의 DateTimeField 에 할당할 필요가 있는 경우 반드시 django.utils.timezone.make_aware() 를 사용해서 time-zone-aware datetime 객체로 변환한 후에 합니다.
  • 참고 링크: Django timezone 문제 파헤치기

freezegun

  • 특정 시점에서의 테스트가 필요한 경우 freezegun 을 사용해서 현재 시간값을 고정합니다.
  • 가급적 decorator 나 context manager 를 사용해서 특정 클래스나 메소드, 혹은 코드 블럭에만 적용하도록 하는 것이 좋습니다.
    • decorator 예제
      from freezegun import freeze_time
      import datetime
      import unittest
       
      @freeze_time("2012-01-14")
      def test():
      assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
      
    • context manager 예제
      from freezegun import freeze_time
       
      def test():
       assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
       with freeze_time("2012-01-14"):
       assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
       assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
      
  • 특정 테스트 케이스 전체에 적용을 하기 위해 start(), stop() 메소드를 사용하기도 하는데 이 경우 반드시 stop() 을 해주어야 다른 테스트 케이스의 시간 값에 영향을 주지 않습니다.
    • 예제
      from django.test import TestCase
      from freezegun import freeze_time
       
      class SomeTestCase(TestCase):
       def setUp(self):
       self.freezer = freeze_time("2016-01-05 00:00:00")
       self.freezer.start()
       
       def tearDown(self):
       self.freezer.stop()
      
  • 참고 링크: freezegun

맺음말

Python Django 개발시 Test case 작성을 잘 하기 위한 8퍼센트 개발팀의 가이드를 공유해 보았습니다. Python Django 개발자들이 Test case 작성을 효율적으로 잘 해서 서비스의 안정성을 높이는데 도움이 되기를 기대해 봅니다.


#8퍼센트 #에잇퍼센트 #Django #Python #장고 #파이썬 #개발 #개발자 #가이드 #꿀팁 #인사이트


관련 스택

8퍼센트 팀의 팀터뷰 보기

‘무엇을 상상하시던 그 이상입니다’ 8퍼센트 김현우 마케팅 팀장

기업문화 엿볼 때, 더팀스

로그인

/