目次
1. Python unittest란?
unittest
는 Python 표준 라이브러리에 포함된 유닛 테스트 프레임워크로, 코드 품질을 보장하기 위해 중요한 도구입니다. 개발자가 코드의 각 부분을 개별적으로 테스트할 수 있게 하며, 초기에 버그를 발견할 수 있도록 합니다. 또한 지속적인 개발 과정에서 코드 변경이 기존 기능을 손상시키지 않았는지 확인하는 데 도움이 됩니다.유닛 테스트의 중요성
코드가 복잡해질수록, 서로 다른 부분이 올바르게 연동되어 동작하는지를 확인하기 어려워집니다. 유닛 테스트를 도입함으로써 작은 변경으로 인한 예상치 못한 버그를 방지하기 쉬워지고, 프로그램 전체의 안정성을 유지할 수 있습니다.2. unittest의 기본적인 사용법
unittest
의 기본은 unittest.TestCase
를 상속한 클래스를 만들고, 그 안에 테스트 메서드를 정의하는 것입니다. 테스트 메서드 안에서 assertEqual()
와 같은 어설션 메서드를 사용하여 기대하는 결과와 실제 결과를 비교합니다。기본적인 테스트 예시</h3음 코드는 add(a, b)
함수를 테스트하는 간단한 예시입니다。
import unittest
# 테스트 대상 코드
def add(a, b):
return a + b
# 테스트 클래스
class TestAddFunction(unittest.TestCase):
def test_add_integers(self):
result = add(2, 3)
self.assertEqual(result, 5)
if __name__ == '__main__':
unittest.main()
이 코드에서는 add()
함수가 올바르게 동작하는지를 테스트합니다. assertEqual()
메서드는 기대값과 실제 결과가 동일함을 확인합니다. 이 방법으로 여러 케이스에 대해 함수가 올바르게 동작함을 확인할 수 있습니다。테스트 확장
여러 메서드를 사용하여 다양한 입력에 대한 함수의 동작을 테스트할 수 있습니다. 예를 들어, 부동소수점 숫자나 문자열 결합을 테스트하는 것도 가능합니다。def test_add_floats(self):
result = add(2.5, 3.5)
self.assertAlmostEqual(result, 6.0, places=2)
def test_add_strings(self):
result = add("Hello, ", "World!")
self.assertEqual(result, "Hello, World!")
이와 같이, 서로 다른 데이터 타입에 대한 함수의 동작도 테스트함으로써, 함수가 다양한 상황에서 올바 동작함을 확인할 수 있습니다。3. setUp()와 tearDown() 사용법
테스트 전후에 특정 처리를 자동으로 실행하려면setUp()
및 tearDown()
메서드를 사용합니다. 이를 통해 테스트가 실행되기 전에 필요한 준비를 하고, 테스트 종료 후에 정리 작업을 수행할 수 있습니다.setUp() 예시
setUp()
메서드는 각 테스트 메서드가 실행되기 전에 반드시 호출되는 메서드로, 공통 초기화 작업을 모아둘 수 있습니다.def setUp(self):
self.temp_value = 42
tearDown() 예시
tearDown()
메서드는 각 테스트 메서드 후에 실행되어 후처리 및 리소스 해제를 수행합니다. 예를 들어, 데이터베이스 연결 해제나 임시 파일 삭제 등에 사용할 수 있습니다.def tearDown(self):
self.temp_value = None
이와 같이 테스트 코드의 중복성을 줄이고, 보다 깔끔한 코드를 유지할 수 있습니다.4. 모크를 사용한 의존 관계 테스트
테스트 대상 코드가 외부 리소스(데이터베이스, API 등)에 의존하고 있는 경우, 그 의존 부분을 모크로 교체함으로써 테스트 실행 속도를 개선하고, 예측 가능한 테스트를 수행할 수 있습니다. Python의unittest.mock
모듈을 사용하면 이것을 쉽게 구현할 수 있습니다.모크 예시
아래 코드에서는time_consuming_function()
라는 오래 걸리는 함수를 모크로 교체하고 있습니다.from unittest.mock import patch
class TestAddFunction(unittest.TestCase):
@patch('my_module.time_consuming_function')
def test_add_with_mock(self, mock_func):
mock_func.return_value = 0
result = add(2, 3)
self.assertEqual(result, 5)
이 예에서는 모크를 사용하여 time_consuming_function
을 호출하지 않고 테스트를 실행합니다. 이를 통해 테스트 시간을 단축하면서 정확한 결과를 얻을 수 있습니다.
5. 예외 처리와 커스텀 어설션
unittest
에서는 예외 처리도 테스트할 수 있습니다. 예를 들어, 특정 상황에서 예외가 올바르게 발생하는지 확인하려면 assertRaises()
를 사용합니다。예외 처리 테스트
다음 예에서는ZeroDivisionError
가 발생하는 것을 확인합니다。def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
divide(1, 0)
이 코드는 divide(1, 0)
가 호출될 때 ZeroDivisionError
가 발생하는지를 테스트합니다。커스텀 어설션 만들기
표준 어설션으로 대응할 수 없는 경우에는, 독자적인 커스텀 어설션 메서드를 만들 수 있습니다。def assertIsPositive(self, value):
self.assertTrue(value > 0, f'{value} is not positive')
커스텀 어설션을 사용하면 보다 구체적인 테스트 시나리오에 대응할 수 있습니다。6. unittest의 테스트 디스커버리 기능
unittest
의 테스트 디스커버리 기능을 사용하면 프로젝트 내의 모든 테스트 파일을 자동으로 찾아 실행할 수 있습니다. 이 기능은 특히 대규모 프로젝트에서 유용합니다.테스트 디스커버리 사용 방법
테스트 디스커버리를 실행하려면, 아래 명령을 사용합니다.python -m unittest discover
이렇게 하면 지정된 디렉터리 내의 모든 test_*.py
파일이 실행됩니다. 파일이나 디렉터리를 지정하려면, 아래와 같이 옵션을 사용합니다.python -m unittest discover -s tests -p "test_*.py"
이 기능을 사용하면 테스트 파일을 개별적으로 지정하는 수고를 줄일 수 있어, 대규모 프로젝트에서도 효율적으로 테스트를 관리할 수 있습니다.7. unittest를 사용한 성능 향상 팁
테스트 실행 속도가 느리면 개발 효율이 떨어집니다. 여기서는unittest
를 사용한 테스트의 성능을 개선하기 위한 몇 가지 팁을 소개합니다.파일 I/O 최적화
파일에 대한 읽기/쓰기가 필요한 테스트는 메모리 내에서 처리함으로써 속도를 높일 수 있습니다.StringIO
를 사용하여 메모리 상에서 파일처럼 동작하는 객체를 생성함으로써 디스크 I/O를 피할 수 있습니다.from io import StringIO
class TestFileOperations(unittest.TestCase):
def test_write_to_memory(self):
output = StringIO()
output.write('Hello, World!')
self.assertEqual(output.getvalue(), 'Hello, World!')
이 방법을 사용하면 파일에 접근해야 하는 테스트라도 테스트 속도를 크게 개선할 수 있습니다.모크 사용하기
외부 리소스에 대한 접근을 최소화하기 위해 모크를 사용하여 테스트를 고속화할 수 있습니다. 이를 통해 네트워크나 데이터베이스 등의 지연을 회피하고 테스트 실행 시간을 단축할 수 있습니다. 다음 예에서는 API 호출을 모크로 교체하고 있습니다.from unittest.mock import MagicMock
class TestApiCall(unittest.TestCase):
def test_api_response(self):
mock_api = MagicMock(return_value={'status': 'success'})
response = mock_api()
self.assertEqual(response['status'], 'success')
이와 같이 외부 리소스에 의존하지 않고 기능을 테스트함으로써 더 빠르고 더 안정적인 테스트 환경을 구축할 수 있습니다.8. 요약 및 다음 단계
이번 기사에서는 Python의unittest
를 사용한 유닛 테스트의 기본부터 셋업 및 티어다운 활용, 모크를 이용한 의존 관계 테스트, 그리고 테스트 성능을 향상시키는 기술까지 폭넓게 다루었습니다.주요 포인트 요약
- 기본적인 사용법:
unittest.TestCase
를 상속하고, 어설션 메서드를 활용하여 테스트를 작성합니다。 - setUp() / tearDown(): 테스트 전후에 공통 처리를 모아 관리함으로써 코드 재사용성과 가독성을 향상시킵니다。
- 모크 활용: 외부 리소스에 의존하지 않고 기능을 테스트할 수 있어 테스트 효율이 크게 향상됩니다。
- 테스트 디스커버리: 대규모 프로젝트에서 테스트 관리를 쉽게 해주는 편리한 기능입니다。
- 성능 향상 기술: 메모리 상에서 처리하거나 모크를 활용함으로써 테스트 실행 시간을 단축할 수 있습니다。
다음 단계
unittest
의 기본을 마스터했다면, 더욱 고급 테스트 방법에도 도전해 보세요. 예를 들어, 여러 입력 데이터를 한 번에 테스트할 수 있는 “파라미터화 테스트”나, 커버리지 도구를 사용해 코드의 테스트 범위를 확인하는 등 프로젝트 전체의 테스트 전략을 강화할 수 있습니다. 또한, pytest
와 같은 다른 테스트 프레임워크에도 눈을 돌려 용도에 맞는 선택지를 넓히는 것도 좋습니다。 테스트는 개발의 중요한 요소입니다. 버그를 조기에 발견하고 코드 품질을 유지하기 위해 테스트를 적극적으로 도입합시다。