Unittest

unittest는 파이썬의 표준 라이브러리에 포함된 단위 테스트 프레임워크.
이 프레임워크는 소프트웨어 개발에서 개별 코드 단위의 정확성을 검증하는 데 사용된다.

주요 특징

  1. 테스트 자동화: unittest는 테스트 케이스를 자동으로 실행하고 결과를 보고한다.
  2. 테스트 독립성: 각 테스트는 독립적으로 실행되며, 다른 테스트의 결과에 영향을 받지 않는다.
  3. 테스트 픽스처: setUp()과 tearDown() 메서드를 통해 테스트 전후 환경을 설정하고 정리할 수 있다.
  4. 단언(Assertions): 다양한 assert 메서드를 제공하여 예상 결과와 실제 결과를 비교할 수 있다.

테스트 케이스 작성

unittest는 unittest.TestCase를 상속하는 클래스를 통해 테스트를 구성한다.
이러한 클래스 기반 접근방식은 다음과 같은 장점을 제공한다:

설정과 정리 메서드

unittest는 여러 수준의 설정과 정리 메서드를 제공한다:

이러한 메서드들은 테스트에 필요한 환경을 준비하고 정리하는데 사용된다.

단언문(Assertions)

unittest는 다양한 검증 메서드를 제공한다:

테스트 실행 제어

unittest는 테스트 실행을 제어하는 여러 기능을 제공한다:

테스트 결과 보고

unittest는 테스트 실행 결과를 다음과 같이 보고한다:

장점

  1. 표준 라이브러리: 별도의 설치 없이 바로 사용 가능하다.
  2. 자동화된 테스트: 테스트 실행과 결과 보고가 자동화되어 있다.
  3. 테스트 구조화: 테스트 케이스와 테스트 스위트를 통해 테스트를 체계적으로 구성할 수 있다.

단점

  1. 문법의 복잡성: 다른 테스트 프레임워크에 비해 상대적으로 복잡한 문법을 가지고 있다.
  2. 제한된 기능: 일부 고급 기능은 제3자 라이브러리를 통해 보완해야 한다.

기본 구조

unittest를 사용한 테스트 코드의 기본 구조는 다음과 같다:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import unittest

class TestExample(unittest.TestCase):
    def setUp(self):
        # 테스트 전 준비 작업

    def test_something(self):
        # 실제 테스트 코드

    def tearDown(self):
        # 테스트 후 정리 작업

if __name__ == '__main__':
    unittest.main()

사용 예제

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# calculator.py
class Calculator:
    def __init__(self):
        self.history = []
        
    def add(self, a, b):
        result = a + b
        self.history.append(f"Added {a} and {b} = {result}")
        return result
    
    def divide(self, a, b):
        if b == 0:
            raise ValueError("Division by zero is not allowed")
        result = a / b
        self.history.append(f"Divided {a} by {b} = {result}")
        return result

# test_calculator.py
import unittest
from calculator import Calculator

class TestCalculator(unittest.TestCase):
    def setUp(self):
        """각 테스트 메서드 실행 전에 호출되는 설정 메서드"""
        self.calc = Calculator()
        self.test_values = [(1, 2), (0, 0), (-1, 1)]
    
    def tearDown(self):
        """각 테스트 메서드 실행 후에 호출되는 정리 메서드"""
        self.calc = None
    
    def test_add(self):
        """덧셈 연산 테스트"""
        # 기본적인 덧셈 테스트
        self.assertEqual(self.calc.add(1, 2), 3)
        self.assertEqual(self.calc.add(-1, 1), 0)
        self.assertEqual(self.calc.add(0, 0), 0)
        
        # 히스토리 기록 테스트
        self.assertIn("Added 1 and 2 = 3", self.calc.history)
    
    def test_divide(self):
        """나눗셈 연산 테스트"""
        # 기본적인 나눗셈 테스트
        self.assertEqual(self.calc.divide(6, 2), 3.0)
        self.assertEqual(self.calc.divide(0, 5), 0.0)
        
        # 부동소수점 나눗셈 테스트
        self.assertAlmostEqual(self.calc.divide(1, 3), 0.333333, places=6)
    
    def test_divide_by_zero(self):
        """0으로 나누기 예외 테스트"""
        with self.assertRaises(ValueError) as context:
            self.calc.divide(1, 0)
        
        self.assertEqual(str(context.exception), "Division by zero is not allowed")
    
    @unittest.skip("이 기능은 아직 구현되지 않았습니다")
    def test_future_feature(self):
        """아직 구현되지 않은 기능 테스트"""
        pass
    
    @unittest.skipIf(sys.version_info.major < 3,
                    "Python 3 이상에서만 실행됩니다")
    def test_python3_feature(self):
        """Python 3 전용 기능 테스트"""
        pass
    
    def test_multiple_operations(self):
        """여러 연산의 조합 테스트"""
        # 첫 번째 연산
        result1 = self.calc.add(5, 3)
        self.assertEqual(result1, 8)
        
        # 두 번째 연산
        result2 = self.calc.divide(result1, 2)
        self.assertEqual(result2, 4.0)
        
        # 히스토리 길이 확인
        self.assertEqual(len(self.calc.history), 2)

class TestCalculatorAdvanced(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        """테스트 클래스 실행 전에 한 번만 호출"""
        print("\n=== 고급 계산기 테스트 시작 ===")
    
    @classmethod
    def tearDownClass(cls):
        """테스트 클래스 실행 후에 한 번만 호출"""
        print("\n=== 고급 계산기 테스트 종료 ===")
    
    def test_floating_point(self):
        """부동소수점 연산 테스트"""
        calc = Calculator()
        self.assertAlmostEqual(calc.add(0.1, 0.2), 0.3, places=7)

if __name__ == '__main__':
    unittest.main()

모범 사례와 권장사항

  1. 테스트 케이스 구성
    • 각 테스트 메서드는 하나의 기능만 테스트
    • 테스트 메서드 이름은 명확하게 작성
    • 관련된 테스트들을 같은 클래스에 그룹화
  2. 독립성 유지
    • 각 테스트는 독립적으로 실행 가능해야 함
    • 테스트 간 의존성 피하기
    • setUp과 tearDown을 적절히 활용
  3. 예외 처리
    • 예외 테스트는 assertRaises 사용
    • 예외 메시지도 함께 검증
    • 적절한 컨텍스트 관리

참고 및 출처

unittest — Unit testing framework — Python 3.13.1 문서